#ifndef AM_CLIPP_H__
#define AM_CLIPP_H__

#include <cstring>
#include <string>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <memory>
#include <vector>
#include <limits>
#include <stack>
#include <algorithm>
#include <sstream>
#include <utility>
#include <iterator>
#include <functional>


 /*************************************************************************//**
  *
  * @brief primary namespace
  *
  *****************************************************************************/
namespace clipp {



	/*****************************************************************************
	 *
	 * basic constants and datatype definitions
	 *
	 *****************************************************************************/
	using arg_index = int;

	using arg_string = std::string;
	using doc_string = std::string;

	using arg_list = std::vector<arg_string>;



	/*************************************************************************//**
	 *
	 * @brief tristate
	 *
	 *****************************************************************************/
	enum class tri : char { no, yes, either };

	inline constexpr bool operator == (tri t, bool b) noexcept {
		return b ? t != tri::no : t != tri::yes;
	}
	inline constexpr bool operator == (bool b, tri t) noexcept { return  (t == b); }
	inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); }
	inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); }



	/*************************************************************************//**
	 *
	 * @brief (start,size) index range
	 *
	 *****************************************************************************/
	class subrange {
	public:
		using size_type = arg_string::size_type;

		/** @brief default: no match */
		explicit constexpr
			subrange() noexcept :
			at_{ arg_string::npos }, length_{ 0 }
		{}

		/** @brief match length & position within subject string */
		explicit constexpr
			subrange(size_type pos, size_type len) noexcept :
			at_{ pos }, length_{ len }
		{}

		/** @brief position of the match within the subject string */
		constexpr size_type at()     const noexcept { return at_; }
		/** @brief length of the matching subsequence */
		constexpr size_type length() const noexcept { return length_; }

		/** @brief returns true, if query string is a prefix of the subject string */
		constexpr bool prefix() const noexcept {
			return at_ == 0;
		}

		/** @brief returns true, if query is a substring of the query string */
		constexpr explicit operator bool() const noexcept {
			return at_ != arg_string::npos;
		}

	private:
		size_type at_;
		size_type length_;
	};



	/*************************************************************************//**
	 *
	 * @brief match predicates
	 *
	 *****************************************************************************/
	using match_predicate = std::function<bool(const arg_string&)>;
	using match_function = std::function<subrange(const arg_string&)>;






	/*************************************************************************//**
	 *
	 * @brief type traits (NOT FOR DIRECT USE IN CLIENT CODE!)
	 *        no interface guarantees; might be changed or removed in the future
	 *
	 *****************************************************************************/
	namespace traits {

		/*************************************************************************//**
		 *
		 * @brief function (class) signature type trait
		 *
		 *****************************************************************************/
		template<class Fn, class Ret, class... Args>
		constexpr auto
			check_is_callable(int) -> decltype(
				std::declval<Fn>()(std::declval<Args>()...),
				std::integral_constant<bool,
				std::is_same<Ret, typename std::result_of<Fn(Args...)>::type>::value>{});

		template<class, class, class...>
		constexpr auto
			check_is_callable(long)->std::false_type;

		template<class Fn, class Ret>
		constexpr auto
			check_is_callable_without_arg(int) -> decltype(
				std::declval<Fn>()(),
				std::integral_constant<bool,
				std::is_same<Ret, typename std::result_of<Fn()>::type>::value>{});

		template<class, class>
		constexpr auto
			check_is_callable_without_arg(long)->std::false_type;



		template<class Fn, class... Args>
		constexpr auto
			check_is_void_callable(int) -> decltype(
				std::declval<Fn>()(std::declval<Args>()...), std::true_type{});

		template<class, class, class...>
		constexpr auto
			check_is_void_callable(long)->std::false_type;

		template<class Fn>
		constexpr auto
			check_is_void_callable_without_arg(int) -> decltype(
				std::declval<Fn>()(), std::true_type{});

		template<class>
		constexpr auto
			check_is_void_callable_without_arg(long)->std::false_type;



		template<class Fn, class Ret>
		struct is_callable;


		template<class Fn, class Ret, class... Args>
		struct is_callable<Fn, Ret(Args...)> :
			decltype(check_is_callable<Fn, Ret, Args...>(0))
		{};

		template<class Fn, class Ret>
		struct is_callable<Fn, Ret()> :
			decltype(check_is_callable_without_arg<Fn, Ret>(0))
		{};


		template<class Fn, class... Args>
		struct is_callable<Fn, void(Args...)> :
			decltype(check_is_void_callable<Fn, Args...>(0))
		{};

		template<class Fn>
		struct is_callable<Fn, void()> :
			decltype(check_is_void_callable_without_arg<Fn>(0))
		{};



		/*************************************************************************//**
		 *
		 * @brief input range type trait
		 *
		 *****************************************************************************/
		template<class T>
		constexpr auto
			check_is_input_range(int) -> decltype(
				begin(std::declval<T>()), end(std::declval<T>()),
				std::true_type{});

		template<class T>
		constexpr auto
			check_is_input_range(char) -> decltype(
				std::begin(std::declval<T>()), std::end(std::declval<T>()),
				std::true_type{});

		template<class>
		constexpr auto
			check_is_input_range(long)->std::false_type;

		template<class T>
		struct is_input_range :
			decltype(check_is_input_range<T>(0))
		{};



		/*************************************************************************//**
		 *
		 * @brief size() member type trait
		 *
		 *****************************************************************************/
		template<class T>
		constexpr auto
			check_has_size_getter(int) ->
			decltype(std::declval<T>().size(), std::true_type{});

		template<class>
		constexpr auto
			check_has_size_getter(long)->std::false_type;

		template<class T>
		struct has_size_getter :
			decltype(check_has_size_getter<T>(0))
		{};

	} // namespace traits






	/*************************************************************************//**
	 *
	 * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
	 *        no interface guarantees; might be changed or removed in the future
	 *
	 *****************************************************************************/
	namespace detail {


		/*************************************************************************//**
		 * @brief forwards string to first non-whitespace char;
		 *        std string -> unsigned conv yields max value, but we want 0;
		 *        also checks for nullptr
		 *****************************************************************************/
		inline bool
			fwd_to_unsigned_int(const char*& s)
		{
			if (!s) return false;
			for (; std::isspace(*s); ++s);
			if (!s[0] || s[0] == '-') return false;
			if (s[0] == '-') return false;
			return true;
		}


		/*************************************************************************//**
		 *
		 * @brief value limits clamping
		 *
		 *****************************************************************************/
		template<class T, class V, bool = (sizeof(V) > sizeof(T))>
		struct limits_clamped {
			static T from(const V& v) {
				if (v >= V(std::numeric_limits<T>::max())) {
					return std::numeric_limits<T>::max();
				}
				if (v <= V(std::numeric_limits<T>::lowest())) {
					return std::numeric_limits<T>::lowest();
				}
				return T(v);
			}
		};

		template<class T, class V>
		struct limits_clamped<T, V, false> {
			static T from(const V& v) { return T(v); }
		};


		/*************************************************************************//**
		 *
		 * @brief returns value of v as a T, clamped at T's maximum
		 *
		 *****************************************************************************/
		template<class T, class V>
		inline T clamped_on_limits(const V& v) {
			return limits_clamped<T, V>::from(v);
		}




		/*************************************************************************//**
		 *
		 * @brief type conversion helpers
		 *
		 *****************************************************************************/
		template<class T>
		struct make {
			static inline T from(const char* s) {
				if (!s) return false;
				//a conversion from const char* to / must exist
				return static_cast<T>(s);
			}
		};

		template<>
		struct make<bool> {
			static inline bool from(const char* s) {
				if (!s) return false;
				return static_cast<bool>(s);
			}
		};

		template<>
		struct make<unsigned char> {
			static inline unsigned char from(const char* s) {
				if (!fwd_to_unsigned_int(s)) return (0);
				return clamped_on_limits<unsigned char>(std::strtoull(s, nullptr, 10));
			}
		};

		template<>
		struct make<unsigned short int> {
			static inline unsigned short int from(const char* s) {
				if (!fwd_to_unsigned_int(s)) return (0);
				return clamped_on_limits<unsigned short int>(std::strtoull(s, nullptr, 10));
			}
		};

		template<>
		struct make<unsigned int> {
			static inline unsigned int from(const char* s) {
				if (!fwd_to_unsigned_int(s)) return (0);
				return clamped_on_limits<unsigned int>(std::strtoull(s, nullptr, 10));
			}
		};

		template<>
		struct make<unsigned long int> {
			static inline unsigned long int from(const char* s) {
				if (!fwd_to_unsigned_int(s)) return (0);
				return clamped_on_limits<unsigned long int>(std::strtoull(s, nullptr, 10));
			}
		};

		template<>
		struct make<unsigned long long int> {
			static inline unsigned long long int from(const char* s) {
				if (!fwd_to_unsigned_int(s)) return (0);
				return clamped_on_limits<unsigned long long int>(std::strtoull(s, nullptr, 10));
			}
		};

		template<>
		struct make<char> {
			static inline char from(const char* s) {
				//parse as single character?
				const auto n = std::strlen(s);
				if (n == 1) return s[0];
				//parse as integer
				return clamped_on_limits<char>(std::strtoll(s, nullptr, 10));
			}
		};

		template<>
		struct make<short int> {
			static inline short int from(const char* s) {
				return clamped_on_limits<short int>(std::strtoll(s, nullptr, 10));
			}
		};

		template<>
		struct make<int> {
			static inline int from(const char* s) {
				return clamped_on_limits<int>(std::strtoll(s, nullptr, 10));
			}
		};

		template<>
		struct make<long int> {
			static inline long int from(const char* s) {
				return clamped_on_limits<long int>(std::strtoll(s, nullptr, 10));
			}
		};

		template<>
		struct make<long long int> {
			static inline long long int from(const char* s) {
				return (std::strtoll(s, nullptr, 10));
			}
		};

		template<>
		struct make<float> {
			static inline float from(const char* s) {
				return (std::strtof(s, nullptr));
			}
		};

		template<>
		struct make<double> {
			static inline double from(const char* s) {
				return (std::strtod(s, nullptr));
			}
		};

		template<>
		struct make<long double> {
			static inline long double from(const char* s) {
				return (std::strtold(s, nullptr));
			}
		};

		template<>
		struct make<std::string> {
			static inline std::string from(const char* s) {
				return std::string(s);
			}
		};



		/*************************************************************************//**
		 *
		 * @brief assigns boolean constant to one or multiple target objects
		 *
		 *****************************************************************************/
		template<class T, class V = T>
		class assign_value
		{
		public:
			template<class X>
			explicit constexpr
				assign_value(T& target, X&& value) noexcept :
				t_{ std::addressof(target) }, v_{ std::forward<X>(value) }
			{}

			void operator () () const {
				if (t_) *t_ = v_;
			}

		private:
			T* t_;
			V v_;
		};



		/*************************************************************************//**
		 *
		 * @brief flips bools
		 *
		 *****************************************************************************/
		class flip_bool
		{
		public:
			explicit constexpr
				flip_bool(bool& target) noexcept :
				b_{ &target }
			{}

			void operator () () const {
				if (b_) *b_ = !*b_;
			}

		private:
			bool* b_;
		};



		/*************************************************************************//**
		 *
		 * @brief increments using operator ++
		 *
		 *****************************************************************************/
		template<class T>
		class increment
		{
		public:
			explicit constexpr
				increment(T& target) noexcept : t_{ std::addressof(target) } {}

			void operator () () const {
				if (t_) ++(*t_);
			}

		private:
			T* t_;
		};



		/*************************************************************************//**
		 *
		 * @brief decrements using operator --
		 *
		 *****************************************************************************/
		template<class T>
		class decrement
		{
		public:
			explicit constexpr
				decrement(T& target) noexcept : t_{ std::addressof(target) } {}

			void operator () () const {
				if (t_) --(*t_);
			}

		private:
			T* t_;
		};



		/*************************************************************************//**
		 *
		 * @brief increments by a fixed amount using operator +=
		 *
		 *****************************************************************************/
		template<class T>
		class increment_by
		{
		public:
			explicit constexpr
				increment_by(T& target, T by) noexcept :
				t_{ std::addressof(target) }, by_{ std::move(by) }
			{}

			void operator () () const {
				if (t_) (*t_) += by_;
			}

		private:
			T* t_;
			T by_;
		};




		/*************************************************************************//**
		 *
		 * @brief makes a value from a string and assigns it to an object
		 *
		 *****************************************************************************/
		template<class T>
		class map_arg_to
		{
		public:
			explicit constexpr
				map_arg_to(T& target) noexcept : t_{ std::addressof(target) } {}

			void operator () (const char* s) const {
				if (t_ && s) *t_ = detail::make<T>::from(s);
			}

		private:
			T* t_;
		};


		//-------------------------------------------------------------------
		/**
		 * @brief specialization for vectors: append element
		 */
		template<class T>
		class map_arg_to<std::vector<T>>
		{
		public:
			map_arg_to(std::vector<T>& target) : t_{ std::addressof(target) } {}

			void operator () (const char* s) const {
				if (t_ && s) t_->push_back(detail::make<T>::from(s));
			}

		private:
			std::vector<T>* t_;
		};


		//-------------------------------------------------------------------
		/**
		 * @brief specialization for bools:
		 *        set to true regardless of string content
		 */
		template<>
		class map_arg_to<bool>
		{
		public:
			map_arg_to(bool& target) : t_{ &target } {}

			void operator () (const char* s) const {
				if (t_ && s) *t_ = true;
			}

		private:
			bool* t_;
		};


	} // namespace detail






	/*************************************************************************//**
	 *
	 * @brief string matching and processing tools
	 *
	 *****************************************************************************/

	namespace str {


		/*************************************************************************//**
		 *
		 * @brief converts string to value of target type 'T'
		 *
		 *****************************************************************************/
		template<class T>
		T make(const arg_string& s)
		{
			return detail::make<T>::from(s);
		}



		/*************************************************************************//**
		 *
		 * @brief removes trailing whitespace from string
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		inline void
			trimr(std::basic_string<C, T, A>& s)
		{
			if (s.empty()) return;

			s.erase(
				std::find_if_not(s.rbegin(), s.rend(),
					[](char c) { return std::isspace(c); }).base(),
				s.end());
		}


		/*************************************************************************//**
		 *
		 * @brief removes leading whitespace from string
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		inline void
			triml(std::basic_string<C, T, A>& s)
		{
			if (s.empty()) return;

			s.erase(
				s.begin(),
				std::find_if_not(s.begin(), s.end(),
					[](char c) { return std::isspace(c); })
			);
		}


		/*************************************************************************//**
		 *
		 * @brief removes leading and trailing whitespace from string
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		inline void
			trim(std::basic_string<C, T, A>& s)
		{
			triml(s);
			trimr(s);
		}


		/*************************************************************************//**
		 *
		 * @brief removes all whitespaces from string
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		inline void
			remove_ws(std::basic_string<C, T, A>& s)
		{
			if (s.empty()) return;

			s.erase(std::remove_if(s.begin(), s.end(),
				[](char c) { return std::isspace(c); }),
				s.end());
		}


		/*************************************************************************//**
		 *
		 * @brief returns true, if the 'prefix' argument
		 *        is a prefix of the 'subject' argument
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		inline bool
			has_prefix(const std::basic_string<C, T, A>& subject,
				const std::basic_string<C, T, A>& prefix)
		{
			if (prefix.size() > subject.size()) return false;
			return subject.find(prefix) == 0;
		}


		/*************************************************************************//**
		 *
		 * @brief returns true, if the 'postfix' argument
		 *        is a postfix of the 'subject' argument
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		inline bool
			has_postfix(const std::basic_string<C, T, A>& subject,
				const std::basic_string<C, T, A>& postfix)
		{
			if (postfix.size() > subject.size()) return false;
			return (subject.size() - postfix.size()) == subject.find(postfix);
		}



		/*************************************************************************//**
		*
		* @brief   returns longest common prefix of several
		*          sequential random access containers
		*
		* @details InputRange require begin and end (member functions or overloads)
		*          the elements of InputRange require a size() member
		*
		*****************************************************************************/
		template<class InputRange>
		auto
			longest_common_prefix(const InputRange& strs)
			-> typename std::decay<decltype(*begin(strs))>::type
		{
			static_assert(traits::is_input_range<InputRange>(),
				"parameter must satisfy the InputRange concept");

			static_assert(traits::has_size_getter<
				typename std::decay<decltype(*begin(strs))>::type>(),
				"elements of input range must have a ::size() member function");

			using std::begin;
			using std::end;

			using item_t = typename std::decay<decltype(*begin(strs))>::type;
			using str_size_t = typename std::decay<decltype(begin(strs)->size())>::type;

			const auto n = size_t(distance(begin(strs), end(strs)));
			if (n < 1) return item_t("");
			if (n == 1) return *begin(strs);

			//length of shortest string
			auto m = std::min_element(begin(strs), end(strs),
				[](const item_t& a, const item_t& b) {
				return a.size() < b.size(); })->size();

				//check each character until we find a mismatch
				for (str_size_t i = 0; i < m; ++i) {
					for (str_size_t j = 1; j < n; ++j) {
						if (strs[j][i] != strs[j - 1][i])
							return strs[0].substr(0, i);
					}
				}
				return strs[0].substr(0, m);
		}



		/*************************************************************************//**
		 *
		 * @brief  returns longest substring range that could be found in 'arg'
		 *
		 * @param  arg         string to be searched in
		 * @param  substrings  range of candidate substrings
		 *
		 *****************************************************************************/
		template<class C, class T, class A, class InputRange>
		subrange
			longest_substring_match(const std::basic_string<C, T, A>& arg,
				const InputRange& substrings)
		{
			using string_t = std::basic_string<C, T, A>;

			static_assert(traits::is_input_range<InputRange>(),
				"parameter must satisfy the InputRange concept");

			static_assert(std::is_same<string_t,
				typename std::decay<decltype(*begin(substrings))>::type>(),
				"substrings must have same type as 'arg'");

			auto i = string_t::npos;
			auto n = string_t::size_type(0);
			for (const auto& s : substrings) {
				auto j = arg.find(s);
				if (j != string_t::npos && s.size() > n) {
					i = j;
					n = s.size();
				}
			}
			return subrange{ i,n };
		}



		/*************************************************************************//**
		 *
		 * @brief  returns longest prefix range that could be found in 'arg'
		 *
		 * @param  arg       string to be searched in
		 * @param  prefixes  range of candidate prefix strings
		 *
		 *****************************************************************************/
		template<class C, class T, class A, class InputRange>
		subrange
			longest_prefix_match(const std::basic_string<C, T, A>& arg,
				const InputRange& prefixes)
		{
			using string_t = std::basic_string<C, T, A>;
			using s_size_t = typename string_t::size_type;

			static_assert(traits::is_input_range<InputRange>(),
				"parameter must satisfy the InputRange concept");

			static_assert(std::is_same<string_t,
				typename std::decay<decltype(*begin(prefixes))>::type>(),
				"prefixes must have same type as 'arg'");

			auto i = string_t::npos;
			auto n = s_size_t(0);
			for (const auto& s : prefixes) {
				auto j = arg.find(s);
				if (j == 0 && s.size() > n) {
					i = 0;
					n = s.size();
				}
			}
			return subrange{ i,n };
		}



		/*************************************************************************//**
		 *
		 * @brief returns the first occurrence of 'query' within 'subject'
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		inline subrange
			substring_match(const std::basic_string<C, T, A>& subject,
				const std::basic_string<C, T, A>& query)
		{
			if (subject.empty() && query.empty()) return subrange(0, 0);
			if (subject.empty() || query.empty()) return subrange{};
			auto i = subject.find(query);
			if (i == std::basic_string<C, T, A>::npos) return subrange{};
			return subrange{ i,query.size() };
		}



		/*************************************************************************//**
		 *
		 * @brief returns first substring match (pos,len) within the input string
		 *        that represents a number
		 *        (with at maximum one decimal point and digit separators)
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		subrange
			first_number_match(std::basic_string<C, T, A> s,
				C digitSeparator = C(','),
				C decimalPoint = C('.'),
				C exponential = C('e'))
		{
			using string_t = std::basic_string<C, T, A>;

			str::trim(s);
			if (s.empty()) return subrange{};

			auto i = s.find_first_of("0123456789+-");
			if (i == string_t::npos) {
				i = s.find(decimalPoint);
				if (i == string_t::npos) return subrange{};
			}

			bool point = false;
			bool sep = false;
			auto exp = string_t::npos;
			auto j = i + 1;
			for (; j < s.size(); ++j) {
				if (s[j] == digitSeparator) {
					if (!sep) sep = true; else break;
				}
				else {
					sep = false;
					if (s[j] == decimalPoint) {
						//only one decimal point before exponent allowed
						if (!point && exp == string_t::npos) point = true; else break;
					}
					else if (std::tolower(s[j]) == std::tolower(exponential)) {
						//only one exponent separator allowed
						if (exp == string_t::npos) exp = j; else break;
					}
					else if (exp != string_t::npos && (exp + 1) == j) {
						//only sign or digit after exponent separator
						if (s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break;
					}
					else if (!std::isdigit(s[j])) {
						break;
					}
				}
			}

			//if length == 1 then must be a digit
			if (j - i == 1 && !std::isdigit(s[i])) return subrange{};

			return subrange{ i,j - i };
		}



		/*************************************************************************//**
		 *
		 * @brief returns first substring match (pos,len)
		 *        that represents an integer (with optional digit separators)
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		subrange
			first_integer_match(std::basic_string<C, T, A> s,
				C digitSeparator = C(','))
		{
			using string_t = std::basic_string<C, T, A>;

			str::trim(s);
			if (s.empty()) return subrange{};

			auto i = s.find_first_of("0123456789+-");
			if (i == string_t::npos) return subrange{};

			bool sep = false;
			auto j = i + 1;
			for (; j < s.size(); ++j) {
				if (s[j] == digitSeparator) {
					if (!sep) sep = true; else break;
				}
				else {
					sep = false;
					if (!std::isdigit(s[j])) break;
				}
			}

			//if length == 1 then must be a digit
			if (j - i == 1 && !std::isdigit(s[i])) return subrange{};

			return subrange{ i,j - i };
		}



		/*************************************************************************//**
		 *
		 * @brief returns true if candidate string represents a number
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		bool represents_number(const std::basic_string<C, T, A>& candidate,
			C digitSeparator = C(','),
			C decimalPoint = C('.'),
			C exponential = C('e'))
		{
			const auto match = str::first_number_match(candidate, digitSeparator,
				decimalPoint, exponential);

			return (match && match.length() == candidate.size());
		}



		/*************************************************************************//**
		 *
		 * @brief returns true if candidate string represents an integer
		 *
		 *****************************************************************************/
		template<class C, class T, class A>
		bool represents_integer(const std::basic_string<C, T, A>& candidate,
			C digitSeparator = C(','))
		{
			const auto match = str::first_integer_match(candidate, digitSeparator);
			return (match && match.length() == candidate.size());
		}

	} // namespace str






	/*************************************************************************//**
	 *
	 * @brief makes function object with a const char* parameter
	 *        that assigns a value to a ref-captured object
	 *
	 *****************************************************************************/
	template<class T, class V>
	inline detail::assign_value<T, V>
		set(T& target, V value) {
		return detail::assign_value<T>{target, std::move(value)};
	}



	/*************************************************************************//**
	 *
	 * @brief makes parameter-less function object
	 *        that assigns value(s) to a ref-captured object;
	 *        value(s) are obtained by converting the const char* argument to
	 *        the captured object types;
	 *        bools are always set to true if the argument is not nullptr
	 *
	 *****************************************************************************/
	template<class T>
	inline detail::map_arg_to<T>
		set(T& target) {
		return detail::map_arg_to<T>{target};
	}



	/*************************************************************************//**
	 *
	 * @brief makes function object that sets a bool to true
	 *
	 *****************************************************************************/
	inline detail::assign_value<bool>
		set(bool& target) {
		return detail::assign_value<bool>{target, true};
	}

	/*************************************************************************//**
	 *
	 * @brief makes function object that sets a bool to false
	 *
	 *****************************************************************************/
	inline detail::assign_value<bool>
		unset(bool& target) {
		return detail::assign_value<bool>{target, false};
	}

	/*************************************************************************//**
	 *
	 * @brief makes function object that flips the value of a ref-captured bool
	 *
	 *****************************************************************************/
	inline detail::flip_bool
		flip(bool& b) {
		return detail::flip_bool(b);
	}





	/*************************************************************************//**
	 *
	 * @brief makes function object that increments using operator ++
	 *
	 *****************************************************************************/
	template<class T>
	inline detail::increment<T>
		increment(T& target) {
		return detail::increment<T>{target};
	}

	/*************************************************************************//**
	 *
	 * @brief makes function object that decrements using operator --
	 *
	 *****************************************************************************/
	template<class T>
	inline detail::increment_by<T>
		increment(T& target, T by) {
		return detail::increment_by<T>{target, std::move(by)};
	}

	/*************************************************************************//**
	 *
	 * @brief makes function object that increments by a fixed amount using operator +=
	 *
	 *****************************************************************************/
	template<class T>
	inline detail::decrement<T>
		decrement(T& target) {
		return detail::decrement<T>{target};
	}






	/*************************************************************************//**
	 *
	 * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
	 *
	 *****************************************************************************/
	namespace detail {


		/*************************************************************************//**
		 *
		 * @brief mixin that provides action definition and execution
		 *
		 *****************************************************************************/
		template<class Derived>
		class action_provider
		{
		private:
			//---------------------------------------------------------------
			using simple_action = std::function<void()>;
			using arg_action = std::function<void(const char*)>;
			using index_action = std::function<void(int)>;

			//-----------------------------------------------------
			class simple_action_adapter {
			public:
				simple_action_adapter() = default;
				simple_action_adapter(const simple_action& a) : action_(a) {}
				simple_action_adapter(simple_action&& a) : action_(std::move(a)) {}
				void operator() (const char*) const { action_(); }
				void operator() (int) const { action_(); }
			private:
				simple_action action_;
			};


		public:
			//---------------------------------------------------------------
			/** @brief adds an action that has an operator() that is callable
			 *         with a 'const char*' argument */
			Derived&
				call(arg_action a) {
				argActions_.push_back(std::move(a));
				return *static_cast<Derived*>(this);
			}

			/** @brief adds an action that has an operator()() */
			Derived&
				call(simple_action a) {
				argActions_.push_back(simple_action_adapter(std::move(a)));
				return *static_cast<Derived*>(this);
			}

			/** @brief adds an action that has an operator() that is callable
			 *         with a 'const char*' argument */
			Derived& operator () (arg_action a) { return call(std::move(a)); }

			/** @brief adds an action that has an operator()() */
			Derived& operator () (simple_action a) { return call(std::move(a)); }


			//---------------------------------------------------------------
			/** @brief adds an action that will set the value of 't' from
			 *         a 'const char*' arg */
			template<class Target>
			Derived&
				set(Target& t) {
				static_assert(!std::is_pointer<Target>::value,
					"parameter target type must not be a pointer");

				return call(clipp::set(t));
			}

			/** @brief adds an action that will set the value of 't' to 'v' */
			template<class Target, class Value>
			Derived&
				set(Target& t, Value&& v) {
				return call(clipp::set(t, std::forward<Value>(v)));
			}


			//---------------------------------------------------------------
			/** @brief adds an action that will be called if a parameter
			 *         matches an argument for the 2nd, 3rd, 4th, ... time
			 */
			Derived&
				if_repeated(simple_action a) {
				repeatActions_.push_back(simple_action_adapter{ std::move(a) });
				return *static_cast<Derived*>(this);
			}
			/** @brief adds an action that will be called with the argument's
			 *         index if a parameter matches an argument for
			 *         the 2nd, 3rd, 4th, ... time
			 */
			Derived&
				if_repeated(index_action a) {
				repeatActions_.push_back(std::move(a));
				return *static_cast<Derived*>(this);
			}


			//---------------------------------------------------------------
			/** @brief adds an action that will be called if a required parameter
			 *         is missing
			 */
			Derived&
				if_missing(simple_action a) {
				missingActions_.push_back(simple_action_adapter{ std::move(a) });
				return *static_cast<Derived*>(this);
			}
			/** @brief adds an action that will be called if a required parameter
			 *         is missing; the action will get called with the index of
			 *         the command line argument where the missing event occurred first
			 */
			Derived&
				if_missing(index_action a) {
				missingActions_.push_back(std::move(a));
				return *static_cast<Derived*>(this);
			}


			//---------------------------------------------------------------
			/** @brief adds an action that will be called if a parameter
			 *         was matched, but was unreachable in the current scope
			 */
			Derived&
				if_blocked(simple_action a) {
				blockedActions_.push_back(simple_action_adapter{ std::move(a) });
				return *static_cast<Derived*>(this);
			}
			/** @brief adds an action that will be called if a parameter
			 *         was matched, but was unreachable in the current scope;
			 *         the action will be called with the index of
			 *         the command line argument where the problem occurred
			 */
			Derived&
				if_blocked(index_action a) {
				blockedActions_.push_back(std::move(a));
				return *static_cast<Derived*>(this);
			}


			//---------------------------------------------------------------
			/** @brief adds an action that will be called if a parameter match
			 *         was in conflict with a different alternative parameter
			 */
			Derived&
				if_conflicted(simple_action a) {
				conflictActions_.push_back(simple_action_adapter{ std::move(a) });
				return *static_cast<Derived*>(this);
			}
			/** @brief adds an action that will be called if a parameter match
			 *         was in conflict with a different alternative parameter;
			 *         the action will be called with the index of
			 *         the command line argument where the problem occurred
			 */
			Derived&
				if_conflicted(index_action a) {
				conflictActions_.push_back(std::move(a));
				return *static_cast<Derived*>(this);
			}


			//---------------------------------------------------------------
			/** @brief adds targets = either objects whose values should be
			 *         set by command line arguments or actions that should
			 *         be called in case of a match */
			template<class T, class... Ts>
			Derived&
				target(T&& t, Ts&&... ts) {
				target(std::forward<T>(t));
				target(std::forward<Ts>(ts)...);
				return *static_cast<Derived*>(this);
			}

			/** @brief adds action that should be called in case of a match */
			template<class T, class = typename std::enable_if<
				!std::is_fundamental<typename std::decay<T>::type>() &&
				(traits::is_callable<T, void()>() ||
					traits::is_callable<T, void(const char*)>())
			>::type>
				Derived&
				target(T&& t) {
				call(std::forward<T>(t));
				return *static_cast<Derived*>(this);
			}

			/** @brief adds object whose value should be set by command line arguments
			 */
			template<class T, class = typename std::enable_if<
				std::is_fundamental<typename std::decay<T>::type>() ||
				(!traits::is_callable<T, void()>() &&
					!traits::is_callable<T, void(const char*)>())
			>::type>
				Derived&
				target(T& t) {
				set(t);
				return *static_cast<Derived*>(this);
			}

			//TODO remove ugly empty param list overload
			Derived&
				target() {
				return *static_cast<Derived*>(this);
			}


			//---------------------------------------------------------------
			/** @brief adds target, see member function 'target' */
			template<class Target>
			inline friend Derived&
				operator << (Target&& t, Derived& p) {
				p.target(std::forward<Target>(t));
				return p;
			}
			/** @brief adds target, see member function 'target' */
			template<class Target>
			inline friend Derived&&
				operator << (Target&& t, Derived&& p) {
				p.target(std::forward<Target>(t));
				return std::move(p);
			}

			//-----------------------------------------------------
			/** @brief adds target, see member function 'target' */
			template<class Target>
			inline friend Derived&
				operator >> (Derived& p, Target&& t) {
				p.target(std::forward<Target>(t));
				return p;
			}
			/** @brief adds target, see member function 'target' */
			template<class Target>
			inline friend Derived&&
				operator >> (Derived&& p, Target&& t) {
				p.target(std::forward<Target>(t));
				return std::move(p);
			}


			//---------------------------------------------------------------
			/** @brief executes all argument actions */
			void execute_actions(const arg_string& arg) const {
				int i = 0;
				for (const auto& a : argActions_) {
					++i;
					a(arg.c_str());
				}
			}

			/** @brief executes repeat actions */
			void notify_repeated(arg_index idx) const {
				for (const auto& a : repeatActions_) a(idx);
			}
			/** @brief executes missing error actions */
			void notify_missing(arg_index idx) const {
				for (const auto& a : missingActions_) a(idx);
			}
			/** @brief executes blocked error actions */
			void notify_blocked(arg_index idx) const {
				for (const auto& a : blockedActions_) a(idx);
			}
			/** @brief executes conflict error actions */
			void notify_conflict(arg_index idx) const {
				for (const auto& a : conflictActions_) a(idx);
			}

		private:
			//---------------------------------------------------------------
			std::vector<arg_action> argActions_;
			std::vector<index_action> repeatActions_;
			std::vector<index_action> missingActions_;
			std::vector<index_action> blockedActions_;
			std::vector<index_action> conflictActions_;
		};






		/*************************************************************************//**
		 *
		 * @brief mixin that provides basic common settings of parameters and groups
		 *
		 *****************************************************************************/
		template<class Derived>
		class token
		{
		public:
			//---------------------------------------------------------------
			using doc_string = clipp::doc_string;


			//---------------------------------------------------------------
			/** @brief returns documentation string */
			const doc_string& doc() const noexcept {
				return doc_;
			}

			/** @brief sets documentations string */
			Derived& doc(const doc_string& txt) {
				doc_ = txt;
				return *static_cast<Derived*>(this);
			}

			/** @brief sets documentations string */
			Derived& doc(doc_string&& txt) {
				doc_ = std::move(txt);
				return *static_cast<Derived*>(this);
			}


			//---------------------------------------------------------------
			/** @brief returns if a group/parameter is repeatable */
			bool repeatable() const noexcept {
				return repeatable_;
			}

			/** @brief sets repeatability of group/parameter */
			Derived& repeatable(bool yes) noexcept {
				repeatable_ = yes;
				return *static_cast<Derived*>(this);
			}


			//---------------------------------------------------------------
			/** @brief returns if a group/parameter is blocking/positional */
			bool blocking() const noexcept {
				return blocking_;
			}

			/** @brief determines, if a group/parameter is blocking/positional */
			Derived& blocking(bool yes) noexcept {
				blocking_ = yes;
				return *static_cast<Derived*>(this);
			}


		private:
			//---------------------------------------------------------------
			doc_string doc_;
			bool repeatable_ = false;
			bool blocking_ = false;
		};




		/*************************************************************************//**
		 *
		 * @brief sets documentation strings on a token
		 *
		 *****************************************************************************/
		template<class T>
		inline T&
			operator % (doc_string docstr, token<T>& p)
		{
			return p.doc(std::move(docstr));
		}
		//---------------------------------------------------------
		template<class T>
		inline T&&
			operator % (doc_string docstr, token<T>&& p)
		{
			return std::move(p.doc(std::move(docstr)));
		}

		//---------------------------------------------------------
		template<class T>
		inline T&
			operator % (token<T>& p, doc_string docstr)
		{
			return p.doc(std::move(docstr));
		}
		//---------------------------------------------------------
		template<class T>
		inline T&&
			operator % (token<T>&& p, doc_string docstr)
		{
			return std::move(p.doc(std::move(docstr)));
		}




		/*************************************************************************//**
		 *
		 * @brief sets documentation strings on a token
		 *
		 *****************************************************************************/
		template<class T>
		inline T&
			doc(doc_string docstr, token<T>& p)
		{
			return p.doc(std::move(docstr));
		}
		//---------------------------------------------------------
		template<class T>
		inline T&&
			doc(doc_string docstr, token<T>&& p)
		{
			return std::move(p.doc(std::move(docstr)));
		}



	} // namespace detail



	/*************************************************************************//**
	 *
	 * @brief contains parameter matching functions and function classes
	 *
	 *****************************************************************************/
	namespace match {


		/*************************************************************************//**
		 *
		 * @brief predicate that is always true
		 *
		 *****************************************************************************/
		inline bool
			any(const arg_string&) { return true; }

		/*************************************************************************//**
		 *
		 * @brief predicate that is always false
		 *
		 *****************************************************************************/
		inline bool
			none(const arg_string&) { return false; }



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the argument string is non-empty string
		 *
		 *****************************************************************************/
		inline bool
			nonempty(const arg_string& s) {
			return !s.empty();
		}



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the argument is a non-empty
		 *        string that consists only of alphanumeric characters
		 *
		 *****************************************************************************/
		inline bool
			alphanumeric(const arg_string& s) {
			if (s.empty()) return false;
			return std::all_of(s.begin(), s.end(), [](char c) {return std::isalnum(c); });
		}



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the argument is a non-empty
		 *        string that consists only of alphabetic characters
		 *
		 *****************************************************************************/
		inline bool
			alphabetic(const arg_string& s) {
			return std::all_of(s.begin(), s.end(), [](char c) {return std::isalpha(c); });
		}



		/*************************************************************************//**
		 *
		 * @brief predicate that returns false if the argument string is
		 *        equal to any string from the exclusion list
		 *
		 *****************************************************************************/
		class none_of
		{
		public:
			none_of(arg_list strs) :
				excluded_{ std::move(strs) }
			{}

			template<class... Strings>
			none_of(arg_string str, Strings&&... strs) :
				excluded_{ std::move(str), std::forward<Strings>(strs)... }
			{}

			template<class... Strings>
			none_of(const char* str, Strings&&... strs) :
				excluded_{ arg_string(str), std::forward<Strings>(strs)... }
			{}

			bool operator () (const arg_string& arg) const {
				return (std::find(begin(excluded_), end(excluded_), arg)
					== end(excluded_));
			}

		private:
			arg_list excluded_;
		};



		/*************************************************************************//**
		 *
		 * @brief predicate that returns the first substring match within the input
		 *        string that rmeepresents a number
		 *        (with at maximum one decimal point and digit separators)
		 *
		 *****************************************************************************/
		class numbers
		{
		public:
			explicit
				numbers(char decimalPoint = '.',
					char digitSeparator = ' ',
					char exponentSeparator = 'e')
				:
				decpoint_{ decimalPoint }, separator_{ digitSeparator },
				exp_{ exponentSeparator }
			{}

			subrange operator () (const arg_string& s) const {
				return str::first_number_match(s, separator_, decpoint_, exp_);
			}

		private:
			char decpoint_;
			char separator_;
			char exp_;
		};



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the input string represents an integer
		 *        (with optional digit separators)
		 *
		 *****************************************************************************/
		class integers {
		public:
			explicit
				integers(char digitSeparator = ' ') : separator_{ digitSeparator } {}

			subrange operator () (const arg_string& s) const {
				return str::first_integer_match(s, separator_);
			}

		private:
			char separator_;
		};



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the input string represents
		 *        a non-negative integer (with optional digit separators)
		 *
		 *****************************************************************************/
		class positive_integers {
		public:
			explicit
				positive_integers(char digitSeparator = ' ') : separator_{ digitSeparator } {}

			subrange operator () (const arg_string& s) const {
				auto match = str::first_integer_match(s, separator_);
				if (!match) return subrange{};
				if (s[match.at()] == '-') return subrange{};
				return match;
			}

		private:
			char separator_;
		};



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the input string
		 *        contains a given substring
		 *
		 *****************************************************************************/
		class substring
		{
		public:
			explicit
				substring(arg_string str) : str_{ std::move(str) } {}

			subrange operator () (const arg_string& s) const {
				return str::substring_match(s, str_);
			}

		private:
			arg_string str_;
		};



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the input string starts
		 *        with a given prefix
		 *
		 *****************************************************************************/
		class prefix {
		public:
			explicit
				prefix(arg_string p) : prefix_{ std::move(p) } {}

			bool operator () (const arg_string& s) const {
				return s.find(prefix_) == 0;
			}

		private:
			arg_string prefix_;
		};



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the input string does not start
		 *        with a given prefix
		 *
		 *****************************************************************************/
		class prefix_not {
		public:
			explicit
				prefix_not(arg_string p) : prefix_{ std::move(p) } {}

			bool operator () (const arg_string& s) const {
				return s.find(prefix_) != 0;
			}

		private:
			arg_string prefix_;
		};


		/** @brief alias for prefix_not */
		using noprefix = prefix_not;



		/*************************************************************************//**
		 *
		 * @brief predicate that returns true if the length of the input string
		 *        is wihtin a given interval
		 *
		 *****************************************************************************/
		class length {
		public:
			explicit
				length(std::size_t exact) :
				min_{ exact }, max_{ exact }
			{}

			explicit
				length(std::size_t min, std::size_t max) :
				min_{ min }, max_{ max }
			{}

			bool operator () (const arg_string& s) const {
				return s.size() >= min_ && s.size() <= max_;
			}

		private:
			std::size_t min_;
			std::size_t max_;
		};


		/*************************************************************************//**
		 *
		 * @brief makes function object that returns true if the input string has a
		 *        given minimum length
		 *
		 *****************************************************************************/
		inline length min_length(std::size_t min)
		{
			return length{ min, arg_string::npos - 1 };
		}

		/*************************************************************************//**
		 *
		 * @brief makes function object that returns true if the input string is
		 *        not longer than a given maximum length
		 *
		 *****************************************************************************/
		inline length max_length(std::size_t max)
		{
			return length{ 0, max };
		}


	} // namespace match





	/*************************************************************************//**
	 *
	 * @brief command line parameter that can match one or many arguments.
	 *
	 *****************************************************************************/
	class parameter :
		public detail::token<parameter>,
		public detail::action_provider<parameter>
	{
		/** @brief adapts a 'match_predicate' to the 'match_function' interface */
		class predicate_adapter {
		public:
			explicit
				predicate_adapter(match_predicate pred) : match_{ std::move(pred) } {}

			subrange operator () (const arg_string& arg) const {
				return match_(arg) ? subrange{ 0,arg.size() } : subrange{};
			}

		private:
			match_predicate match_;
		};

	public:
		//---------------------------------------------------------------
		/** @brief makes default parameter, that will match nothing */
		parameter() :
			flags_{},
			matcher_{ predicate_adapter{match::none} },
			label_{}, required_{ false }, greedy_{ false }
		{}

		/** @brief makes "flag" parameter */
		template<class... Strings>
		explicit
			parameter(arg_string str, Strings&&... strs) :
			flags_{},
			matcher_{ predicate_adapter{match::none} },
			label_{}, required_{ false }, greedy_{ false }
		{
			add_flags(std::move(str), std::forward<Strings>(strs)...);
		}

		/** @brief makes "flag" parameter from range of strings */
		explicit
			parameter(const arg_list& flaglist) :
			flags_{},
			matcher_{ predicate_adapter{match::none} },
			label_{}, required_{ false }, greedy_{ false }
		{
			add_flags(flaglist);
		}

		//-----------------------------------------------------
		/** @brief makes "value" parameter with custom match predicate
		 *         (= yes/no matcher)
		 */
		explicit
			parameter(match_predicate filter) :
			flags_{},
			matcher_{ predicate_adapter{std::move(filter)} },
			label_{}, required_{ false }, greedy_{ false }
		{}

		/** @brief makes "value" parameter with custom match function
		 *         (= partial matcher)
		 */
		explicit
			parameter(match_function filter) :
			flags_{},
			matcher_{ std::move(filter) },
			label_{}, required_{ false }, greedy_{ false }
		{}


		//---------------------------------------------------------------
		/** @brief returns if a parameter is required */
		bool
			required() const noexcept {
			return required_;
		}

		/** @brief determines if a parameter is required */
		parameter&
			required(bool yes) noexcept {
			required_ = yes;
			return *this;
		}


		//---------------------------------------------------------------
		/** @brief returns if a parameter should match greedily */
		bool
			greedy() const noexcept {
			return greedy_;
		}

		/** @brief determines if a parameter should match greedily */
		parameter&
			greedy(bool yes) noexcept {
			greedy_ = yes;
			return *this;
		}


		//---------------------------------------------------------------
		/** @brief returns parameter label;
		 *         will be used for documentation, if flags are empty
		 */
		const doc_string&
			label() const {
			return label_;
		}

		/** @brief sets parameter label;
		 *         will be used for documentation, if flags are empty
		 */
		parameter&
			label(const doc_string& lbl) {
			label_ = lbl;
			return *this;
		}

		/** @brief sets parameter label;
		 *         will be used for documentation, if flags are empty
		 */
		parameter&
			label(doc_string&& lbl) {
			label_ = lbl;
			return *this;
		}


		//---------------------------------------------------------------
		/** @brief returns either longest matching prefix of 'arg' in any
		 *         of the flags or the result of the custom match operation
		 */
		subrange
			match(const arg_string& arg) const
		{
			if (flags_.empty()) {
				return matcher_(arg);
			}
			else {
				//empty flags are not allowed
				if (arg.empty()) return subrange{};

				if (std::find(flags_.begin(), flags_.end(), arg) != flags_.end()) {
					return subrange{ 0,arg.size() };
				}
				return str::longest_prefix_match(arg, flags_);
			}
		}


		//---------------------------------------------------------------
		/** @brief access range of flag strings */
		const arg_list&
			flags() const noexcept {
			return flags_;
		}

		/** @brief access custom match operation */
		const match_function&
			matcher() const noexcept {
			return matcher_;
		}


		//---------------------------------------------------------------
		/** @brief prepend prefix to each flag */
		inline friend parameter&
			with_prefix(const arg_string& prefix, parameter& p)
		{
			if (prefix.empty() || p.flags().empty()) return p;

			for (auto& f : p.flags_) {
				if (f.find(prefix) != 0) f.insert(0, prefix);
			}
			return p;
		}


		/** @brief prepend prefix to each flag
		 */
		inline friend parameter&
			with_prefixes_short_long(
				const arg_string& shortpfx, const arg_string& longpfx,
				parameter& p)
		{
			if (shortpfx.empty() && longpfx.empty()) return p;
			if (p.flags().empty()) return p;

			for (auto& f : p.flags_) {
				if (f.size() == 1) {
					if (f.find(shortpfx) != 0) f.insert(0, shortpfx);
				}
				else {
					if (f.find(longpfx) != 0) f.insert(0, longpfx);
				}
			}
			return p;
		}


		//---------------------------------------------------------------
		/** @brief prepend suffix to each flag */
		inline friend parameter&
			with_suffix(const arg_string& suffix, parameter& p)
		{
			if (suffix.empty() || p.flags().empty()) return p;

			for (auto& f : p.flags_) {
				if (f.find(suffix) + suffix.size() != f.size()) {
					f.insert(f.end(), suffix.begin(), suffix.end());
				}
			}
			return p;
		}


		/** @brief prepend suffix to each flag
		 */
		inline friend parameter&
			with_suffixes_short_long(
				const arg_string& shortsfx, const arg_string& longsfx,
				parameter& p)
		{
			if (shortsfx.empty() && longsfx.empty()) return p;
			if (p.flags().empty()) return p;

			for (auto& f : p.flags_) {
				if (f.size() == 1) {
					if (f.find(shortsfx) + shortsfx.size() != f.size()) {
						f.insert(f.end(), shortsfx.begin(), shortsfx.end());
					}
				}
				else {
					if (f.find(longsfx) + longsfx.size() != f.size()) {
						f.insert(f.end(), longsfx.begin(), longsfx.end());
					}
				}
			}
			return p;
		}

	private:
		//---------------------------------------------------------------
		void add_flags(arg_string str) {
			//empty flags are not allowed
			str::remove_ws(str);
			if (!str.empty()) flags_.push_back(std::move(str));
		}

		//---------------------------------------------------------------
		void add_flags(const arg_list& strs) {
			if (strs.empty()) return;
			flags_.reserve(flags_.size() + strs.size());
			for (const auto& s : strs) add_flags(s);
		}

		template<class String1, class String2, class... Strings>
		void
			add_flags(String1&& s1, String2&& s2, Strings&&... ss) {
			flags_.reserve(2 + sizeof...(ss));
			add_flags(std::forward<String1>(s1));
			add_flags(std::forward<String2>(s2), std::forward<Strings>(ss)...);
		}

		arg_list flags_;
		match_function matcher_;
		doc_string label_;
		bool required_ = false;
		bool greedy_ = false;
	};




	/*************************************************************************//**
	 *
	 * @brief makes required non-blocking exact match parameter
	 *
	 *****************************************************************************/
	template<class String, class... Strings>
	inline parameter
		command(String&& flag, Strings&&... flags)
	{
		return parameter{ std::forward<String>(flag), std::forward<Strings>(flags)... }
		.required(true).blocking(true).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required non-blocking exact match parameter
	 *
	 *****************************************************************************/
	template<class String, class... Strings>
	inline parameter
		required(String&& flag, Strings&&... flags)
	{
		return parameter{ std::forward<String>(flag), std::forward<Strings>(flags)... }
		.required(true).blocking(false).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, non-blocking exact match parameter
	 *
	 *****************************************************************************/
	template<class String, class... Strings>
	inline parameter
		option(String&& flag, Strings&&... flags)
	{
		return parameter{ std::forward<String>(flag), std::forward<Strings>(flags)... }
		.required(false).blocking(false).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking, repeatable value parameter;
	 *        matches any non-empty string
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		value(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::nonempty }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(false);
	}

	template<class Filter, class... Targets, class = typename std::enable_if<
		traits::is_callable<Filter, bool(const char*)>::value ||
		traits::is_callable<Filter, subrange(const char*)>::value>::type>
		inline parameter
		value(Filter&& filter, doc_string label, Targets&&... tgts)
	{
		return parameter{ std::forward<Filter>(filter) }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking, repeatable value parameter;
	 *        matches any non-empty string
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		values(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::nonempty }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(true);
	}

	template<class Filter, class... Targets, class = typename std::enable_if<
		traits::is_callable<Filter, bool(const char*)>::value ||
		traits::is_callable<Filter, subrange(const char*)>::value>::type>
		inline parameter
		values(Filter&& filter, doc_string label, Targets&&... tgts)
	{
		return parameter{ std::forward<Filter>(filter) }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking value parameter;
	 *        matches any non-empty string
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_value(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::nonempty }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(false);
	}

	template<class Filter, class... Targets, class = typename std::enable_if<
		traits::is_callable<Filter, bool(const char*)>::value ||
		traits::is_callable<Filter, subrange(const char*)>::value>::type>
		inline parameter
		opt_value(Filter&& filter, doc_string label, Targets&&... tgts)
	{
		return parameter{ std::forward<Filter>(filter) }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking, repeatable value parameter;
	 *        matches any non-empty string
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_values(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::nonempty }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(true);
	}

	template<class Filter, class... Targets, class = typename std::enable_if<
		traits::is_callable<Filter, bool(const char*)>::value ||
		traits::is_callable<Filter, subrange(const char*)>::value>::type>
		inline parameter
		opt_values(Filter&& filter, doc_string label, Targets&&... tgts)
	{
		return parameter{ std::forward<Filter>(filter) }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking value parameter;
	 *        matches any string consisting of alphanumeric characters
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		word(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::alphanumeric }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking, repeatable value parameter;
	 *        matches any string consisting of alphanumeric characters
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		words(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::alphanumeric }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking value parameter;
	 *        matches any string consisting of alphanumeric characters
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_word(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::alphanumeric }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking, repeatable value parameter;
	 *        matches any string consisting of alphanumeric characters
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_words(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::alphanumeric }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking value parameter;
	 *        matches any string that represents a number
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		number(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::numbers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking, repeatable value parameter;
	 *        matches any string that represents a number
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		numbers(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::numbers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking value parameter;
	 *        matches any string that represents a number
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_number(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::numbers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking, repeatable value parameter;
	 *        matches any string that represents a number
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_numbers(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::numbers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking value parameter;
	 *        matches any string that represents an integer
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		integer(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::integers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes required, blocking, repeatable value parameter;
	 *        matches any string that represents an integer
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		integers(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::integers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(true).blocking(true).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking value parameter;
	 *        matches any string that represents an integer
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_integer(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::integers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes optional, blocking, repeatable value parameter;
	 *        matches any string that represents an integer
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		opt_integers(const doc_string& label, Targets&&... tgts)
	{
		return parameter{ match::integers{} }
			.label(label)
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes catch-all value parameter
	 *
	 *****************************************************************************/
	template<class... Targets>
	inline parameter
		any_other(Targets&&... tgts)
	{
		return parameter{ match::any }
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes catch-all value parameter with custom filter
	 *
	 *****************************************************************************/
	template<class Filter, class... Targets, class = typename std::enable_if<
		traits::is_callable<Filter, bool(const char*)>::value ||
		traits::is_callable<Filter, subrange(const char*)>::value>::type>
		inline parameter
		any(Filter&& filter, Targets&&... tgts)
	{
		return parameter{ std::forward<Filter>(filter) }
			.target(std::forward<Targets>(tgts)...)
			.required(false).blocking(false).repeatable(true);
	}




	/*************************************************************************//**
	 *
	 * @brief group of parameters and/or other groups;
	 *        can be configured to act as a group of alternatives (exclusive match)
	 *
	 *****************************************************************************/
	class group :
		public detail::token<group>
	{
		//---------------------------------------------------------------
		/**
			* @brief tagged union type that either stores a parameter or a group
			*        and provides a common interface to them
			*        could be replaced by std::variant in the future
			*
			*        Note to future self: do NOT try again to do this with
			*        dynamic polymorphism; there are a couple of
			*        nasty problems associated with it and the implementation
			*        becomes bloated and needlessly complicated.
			*/
		template<class Param, class Group>
		struct child_t {
			enum class type : char { param, group };
		public:

			explicit
				child_t(const Param&  v) : m_{ v }, type_{ type::param } {}
			child_t(Param&& v) noexcept : m_{ std::move(v) }, type_{ type::param } {}

			explicit
				child_t(const Group&  g) : m_{ g }, type_{ type::group } {}
			child_t(Group&& g) noexcept : m_{ std::move(g) }, type_{ type::group } {}

			child_t(const child_t& src) : type_{ src.type_ } {
				switch (type_) {
				default:
				case type::param: new(&m_)data{ src.m_.param }; break;
				case type::group: new(&m_)data{ src.m_.group }; break;
				}
			}

			child_t(child_t&& src) noexcept : type_{ src.type_ } {
				switch (type_) {
				default:
				case type::param: new(&m_)data{ std::move(src.m_.param) }; break;
				case type::group: new(&m_)data{ std::move(src.m_.group) }; break;
				}
			}

			child_t& operator = (const child_t& src) {
				destroy_content();
				type_ = src.type_;
				switch (type_) {
				default:
				case type::param: new(&m_)data{ src.m_.param }; break;
				case type::group: new(&m_)data{ src.m_.group }; break;
				}
				return *this;
			}

			child_t& operator = (child_t&& src) noexcept {
				destroy_content();
				type_ = src.type_;
				switch (type_) {
				default:
				case type::param: new(&m_)data{ std::move(src.m_.param) }; break;
				case type::group: new(&m_)data{ std::move(src.m_.group) }; break;
				}
				return *this;
			}

			~child_t() {
				destroy_content();
			}

			const doc_string&
				doc() const noexcept {
				switch (type_) {
				default:
				case type::param: return m_.param.doc();
				case type::group: return m_.group.doc();
				}
			}

			bool blocking() const noexcept {
				switch (type_) {
				case type::param: return m_.param.blocking();
				case type::group: return m_.group.blocking();
				default: return false;
				}
			}
			bool repeatable() const noexcept {
				switch (type_) {
				case type::param: return m_.param.repeatable();
				case type::group: return m_.group.repeatable();
				default: return false;
				}
			}
			bool required() const noexcept {
				switch (type_) {
				case type::param: return m_.param.required();
				case type::group:
					return (m_.group.exclusive() && m_.group.all_required()) ||
						(!m_.group.exclusive() && m_.group.any_required());
				default: return false;
				}
			}
			bool exclusive() const noexcept {
				switch (type_) {
				case type::group: return m_.group.exclusive();
				case type::param:
				default: return false;
				}
			}
			std::size_t param_count() const noexcept {
				switch (type_) {
				case type::group: return m_.group.param_count();
				case type::param:
				default: return std::size_t(1);
				}
			}
			std::size_t depth() const noexcept {
				switch (type_) {
				case type::group: return m_.group.depth();
				case type::param:
				default: return std::size_t(0);
				}
			}

			void execute_actions(const arg_string& arg) const {
				switch (type_) {
				default:
				case type::group: return;
				case type::param: m_.param.execute_actions(arg); break;
				}

			}

			void notify_repeated(arg_index idx) const {
				switch (type_) {
				default:
				case type::group: return;
				case type::param: m_.param.notify_repeated(idx); break;
				}
			}
			void notify_missing(arg_index idx) const {
				switch (type_) {
				default:
				case type::group: return;
				case type::param: m_.param.notify_missing(idx); break;
				}
			}
			void notify_blocked(arg_index idx) const {
				switch (type_) {
				default:
				case type::group: return;
				case type::param: m_.param.notify_blocked(idx); break;
				}
			}
			void notify_conflict(arg_index idx) const {
				switch (type_) {
				default:
				case type::group: return;
				case type::param: m_.param.notify_conflict(idx); break;
				}
			}

			bool is_param() const noexcept { return type_ == type::param; }
			bool is_group() const noexcept { return type_ == type::group; }

			Param& as_param() noexcept { return m_.param; }
			Group& as_group() noexcept { return m_.group; }

			const Param& as_param() const noexcept { return m_.param; }
			const Group& as_group() const noexcept { return m_.group; }

		private:
			void destroy_content() {
				switch (type_) {
				default:
				case type::param: m_.param.~Param(); break;
				case type::group: m_.group.~Group(); break;
				}
			}

			union data {
				data() {}

				data(const Param&  v) : param{ v } {}
				data(Param&& v) noexcept : param{ std::move(v) } {}

				data(const Group&  g) : group{ g } {}
				data(Group&& g) noexcept : group{ std::move(g) } {}
				~data() {}

				Param param;
				Group group;
			};

			data m_;
			type type_;
		};


	public:
		//---------------------------------------------------------------
		using child = child_t<parameter, group>;
		using value_type = child;

	private:
		using children_store = std::vector<child>;

	public:
		using const_iterator = children_store::const_iterator;
		using iterator = children_store::iterator;
		using size_type = children_store::size_type;


		//---------------------------------------------------------------
		/**
		 * @brief recursively iterates over all nodes
		 */
		class depth_first_traverser
		{
		public:
			//-----------------------------------------------------
			struct context {
				context() = default;
				context(const group& p) :
					parent{ &p }, cur{ p.begin() }, end{ p.end() }
				{}
				const group* parent = nullptr;
				const_iterator cur;
				const_iterator end;
			};
			using context_list = std::vector<context>;

			//-----------------------------------------------------
			class memento {
				friend class depth_first_traverser;
				int level_;
				context context_;
			public:
				int level() const noexcept { return level_; }
				const child* param() const noexcept { return &(*context_.cur); }
			};

			depth_first_traverser() = default;

			explicit
				depth_first_traverser(const group& cur) : stack_{} {
				if (!cur.empty()) stack_.emplace_back(cur);
			}

			explicit operator bool() const noexcept {
				return !stack_.empty();
			}

			int level() const noexcept {
				return int(stack_.size());
			}

			bool is_first_in_parent() const noexcept {
				if (stack_.empty()) return false;
				return (stack_.back().cur == stack_.back().parent->begin());
			}

			bool is_last_in_parent() const noexcept {
				if (stack_.empty()) return false;
				return (stack_.back().cur + 1 == stack_.back().end);
			}

			bool is_last_in_path() const noexcept {
				if (stack_.empty()) return false;
				for (const auto& t : stack_) {
					if (t.cur + 1 != t.end) return false;
				}
				const auto& top = stack_.back();
				//if we have to descend into group on next ++ => not last in path
				if (top.cur->is_group()) return false;
				return true;
			}

			/** @brief inside a group of alternatives >= minlevel */
			bool is_alternative(int minlevel = 0) const noexcept {
				if (stack_.empty()) return false;
				if (minlevel > 0) minlevel -= 1;
				if (minlevel >= int(stack_.size())) return false;
				return std::any_of(stack_.begin() + minlevel, stack_.end(),
					[](const context& c) { return c.parent->exclusive(); });
			}

			/** @brief repeatable or inside a repeatable group >= minlevel */
			bool is_repeatable(int minlevel = 0) const noexcept {
				if (stack_.empty()) return false;
				if (stack_.back().cur->repeatable()) return true;
				if (minlevel > 0) minlevel -= 1;
				if (minlevel >= int(stack_.size())) return false;
				return std::any_of(stack_.begin() + minlevel, stack_.end(),
					[](const context& c) { return c.parent->repeatable(); });
			}

			/** @brief inside a particular group */
			bool is_inside(const group* g) const noexcept {
				if (!g) return false;
				return std::any_of(stack_.begin(), stack_.end(),
					[g](const context& c) { return c.parent == g; });
			}

			/** @brief inside group with joinable flags */
			bool joinable() const noexcept {
				if (stack_.empty()) return false;
				return std::any_of(stack_.begin(), stack_.end(),
					[](const context& c) { return c.parent->joinable(); });
			}

			const context_list&
				stack() const {
				return stack_;
			}

			/** @brief innermost repeat group */
			const group*
				innermost_repeat_group() const noexcept {
				auto i = std::find_if(stack_.rbegin(), stack_.rend(),
					[](const context& c) { return c.parent->repeatable(); });
				return i != stack_.rend() ? i->parent : nullptr;
			}

			/** @brief innermost exclusive (alternatives) group */
			const group*
				innermost_exclusive_group() const noexcept {
				auto i = std::find_if(stack_.rbegin(), stack_.rend(),
					[](const context& c) { return c.parent->exclusive(); });
				return i != stack_.rend() ? i->parent : nullptr;
			}

			/** @brief innermost blocking group */
			const group*
				innermost_blocking_group() const noexcept {
				auto i = std::find_if(stack_.rbegin(), stack_.rend(),
					[](const context& c) { return c.parent->blocking(); });
				return i != stack_.rend() ? i->parent : nullptr;
			}

			/** @brief returns the outermost group that will be left on next ++*/
			const group*
				outermost_blocking_group_fully_explored() const noexcept {
				if (stack_.empty()) return nullptr;

				const group* g = nullptr;
				for (auto i = stack_.rbegin(); i != stack_.rend(); ++i) {
					if (i->cur + 1 == i->end) {
						if (i->parent->blocking()) g = i->parent;
					}
					else {
						return g;
					}
				}
				return g;
			}

			/** @brief outermost join group */
			const group*
				outermost_join_group() const noexcept {
				auto i = std::find_if(stack_.begin(), stack_.end(),
					[](const context& c) { return c.parent->joinable(); });
				return i != stack_.end() ? i->parent : nullptr;
			}

			const group* root() const noexcept {
				return stack_.empty() ? nullptr : stack_.front().parent;
			}

			/** @brief common flag prefix of all flags in current group */
			arg_string common_flag_prefix() const noexcept {
				if (stack_.empty()) return "";
				auto g = outermost_join_group();
				return g ? g->common_flag_prefix() : arg_string("");
			}

			const child&
				operator * () const noexcept {
				return *stack_.back().cur;
			}

			const child*
				operator -> () const noexcept {
				return &(*stack_.back().cur);
			}

			const group&
				parent() const noexcept {
				return *(stack_.back().parent);
			}


			/** @brief go to next element of depth first search */
			depth_first_traverser&
				operator ++ () {
				if (stack_.empty()) return *this;
				//at group -> decend into group
				if (stack_.back().cur->is_group()) {
					stack_.emplace_back(stack_.back().cur->as_group());
				}
				else {
					next_sibling();
				}
				return *this;
			}

			/** @brief go to next sibling of current */
			depth_first_traverser&
				next_sibling() {
				if (stack_.empty()) return *this;
				++stack_.back().cur;
				//at the end of current group?
				while (stack_.back().cur == stack_.back().end) {
					//go to parent
					stack_.pop_back();
					if (stack_.empty()) return *this;
					//go to next sibling in parent
					++stack_.back().cur;
				}
				return *this;
			}

			/** @brief go to next position after siblings of current */
			depth_first_traverser&
				next_after_siblings() {
				if (stack_.empty()) return *this;
				stack_.back().cur = stack_.back().end - 1;
				next_sibling();
				return *this;
			}

			/**
			 * @brief
			 */
			depth_first_traverser&
				back_to_ancestor(const group* g) {
				if (!g) return *this;
				while (!stack_.empty()) {
					const auto& top = stack_.back().cur;
					if (top->is_group() && &(top->as_group()) == g) return *this;
					stack_.pop_back();
				}
				return *this;
			}

			/** @brief don't visit next siblings, go back to parent on next ++
			 *         note: renders siblings unreachable for *this
			 **/
			depth_first_traverser&
				skip_siblings() {
				if (stack_.empty()) return *this;
				//future increments won't visit subsequent siblings:
				stack_.back().end = stack_.back().cur + 1;
				return *this;
			}

			/** @brief skips all other alternatives in surrounding exclusive groups
			 *         on next ++
			 *         note: renders alternatives unreachable for *this
			*/
			depth_first_traverser&
				skip_alternatives() {
				if (stack_.empty()) return *this;

				//exclude all other alternatives in surrounding groups
				//by making their current position the last one
				for (auto& c : stack_) {
					if (c.parent && c.parent->exclusive() && c.cur < c.end)
						c.end = c.cur + 1;
				}

				return *this;
			}

			void invalidate() {
				stack_.clear();
			}

			inline friend bool operator == (const depth_first_traverser& a,
				const depth_first_traverser& b)
			{
				if (a.stack_.empty() || b.stack_.empty()) return false;

				//parents not the same -> different position
				if (a.stack_.back().parent != b.stack_.back().parent) return false;

				bool aEnd = a.stack_.back().cur == a.stack_.back().end;
				bool bEnd = b.stack_.back().cur == b.stack_.back().end;
				//either both at the end of the same parent => same position
				if (aEnd && bEnd) return true;
				//or only one at the end => not at the same position
				if (aEnd || bEnd) return false;
				return std::addressof(*a.stack_.back().cur) ==
					std::addressof(*b.stack_.back().cur);
			}
			inline friend bool operator != (const depth_first_traverser& a,
				const depth_first_traverser& b)
			{
				return !(a == b);
			}

			memento
				undo_point() const {
				memento m;
				m.level_ = int(stack_.size());
				if (!stack_.empty()) m.context_ = stack_.back();
				return m;
			}

			void undo(const memento& m) {
				if (m.level_ < 1) return;
				if (m.level_ <= int(stack_.size())) {
					stack_.erase(stack_.begin() + m.level_, stack_.end());
					stack_.back() = m.context_;
				}
				else if (stack_.empty() && m.level_ == 1) {
					stack_.push_back(m.context_);
				}
			}

		private:
			context_list stack_;
		};


		//---------------------------------------------------------------
		group() = default;

		template<class Param, class... Params>
		explicit
			group(doc_string docstr, Param param, Params... params) :
			children_{}, exclusive_{ false }, joinable_{ false }, scoped_{ true }
		{
			doc(std::move(docstr));
			push_back(std::move(param), std::move(params)...);
		}

		template<class... Params>
		explicit
			group(parameter param, Params... params) :
			children_{}, exclusive_{ false }, joinable_{ false }, scoped_{ true }
		{
			push_back(std::move(param), std::move(params)...);
		}

		template<class P2, class... Ps>
		explicit
			group(group p1, P2 p2, Ps... ps) :
			children_{}, exclusive_{ false }, joinable_{ false }, scoped_{ true }
		{
			push_back(std::move(p1), std::move(p2), std::move(ps)...);
		}


		//-----------------------------------------------------
		group(const group&) = default;
		group(group&&) = default;


		//---------------------------------------------------------------
		group& operator = (const group&) = default;
		group& operator = (group&&) = default;


		//---------------------------------------------------------------
		/** @brief determines if a command line argument can be matched by a
		 *         combination of (partial) matches through any number of children
		 */
		group& joinable(bool yes) {
			joinable_ = yes;
			return *this;
		}

		/** @brief returns if a command line argument can be matched by a
		 *         combination of (partial) matches through any number of children
		 */
		bool joinable() const noexcept {
			return joinable_;
		}


		//---------------------------------------------------------------
		/** @brief turns explicit scoping on or off
		 *         operators , & | and other combinating functions will
		 *         not merge groups that are marked as scoped
		 */
		group& scoped(bool yes) {
			scoped_ = yes;
			return *this;
		}

		/** @brief returns true if operators , & | and other combinating functions
		 *         will merge groups and false otherwise
		 */
		bool scoped() const noexcept
		{
			return scoped_;
		}


		//---------------------------------------------------------------
		/** @brief determines if children are mutually exclusive alternatives */
		group& exclusive(bool yes) {
			exclusive_ = yes;
			return *this;
		}
		/** @brief returns if children are mutually exclusive alternatives */
		bool exclusive() const noexcept {
			return exclusive_;
		}


		//---------------------------------------------------------------
		/** @brief returns true, if any child is required to match */
		bool any_required() const
		{
			return std::any_of(children_.begin(), children_.end(),
				[](const child& n) { return n.required(); });
		}
		/** @brief returns true, if all children are required to match */
		bool all_required() const
		{
			return std::all_of(children_.begin(), children_.end(),
				[](const child& n) { return n.required(); });
		}


		//---------------------------------------------------------------
		/** @brief returns true if any child is optional (=non-required) */
		bool any_optional() const {
			return !all_required();
		}
		/** @brief returns true if all children are optional (=non-required) */
		bool all_optional() const {
			return !any_required();
		}


		//---------------------------------------------------------------
		/** @brief returns if the entire group is blocking / positional */
		bool blocking() const noexcept {
			return token<group>::blocking() || (exclusive() && all_blocking());
		}
		//-----------------------------------------------------
		/** @brief determines if the entire group is blocking / positional */
		group& blocking(bool yes) {
			return token<group>::blocking(yes);
		}

		//---------------------------------------------------------------
		/** @brief returns true if any child is blocking */
		bool any_blocking() const
		{
			return std::any_of(children_.begin(), children_.end(),
				[](const child& n) { return n.blocking(); });
		}
		//---------------------------------------------------------------
		/** @brief returns true if all children is blocking */
		bool all_blocking() const
		{
			return std::all_of(children_.begin(), children_.end(),
				[](const child& n) { return n.blocking(); });
		}


		//---------------------------------------------------------------
		/** @brief returns if any child is a value parameter (recursive) */
		bool any_flagless() const
		{
			return std::any_of(children_.begin(), children_.end(),
				[](const child& p) {
				return p.is_param() && p.as_param().flags().empty();
			});
		}
		/** @brief returns if all children are value parameters (recursive) */
		bool all_flagless() const
		{
			return std::all_of(children_.begin(), children_.end(),
				[](const child& p) {
				return p.is_param() && p.as_param().flags().empty();
			});
		}


		//---------------------------------------------------------------
		/** @brief adds child parameter at the end */
		group&
			push_back(const parameter& v) {
			children_.emplace_back(v);
			return *this;
		}
		//-----------------------------------------------------
		/** @brief adds child parameter at the end */
		group&
			push_back(parameter&& v) {
			children_.emplace_back(std::move(v));
			return *this;
		}
		//-----------------------------------------------------
		/** @brief adds child group at the end */
		group&
			push_back(const group& g) {
			children_.emplace_back(g);
			return *this;
		}
		//-----------------------------------------------------
		/** @brief adds child group at the end */
		group&
			push_back(group&& g) {
			children_.emplace_back(std::move(g));
			return *this;
		}


		//-----------------------------------------------------
		/** @brief adds children (groups and/or parameters) */
		template<class Param1, class Param2, class... Params>
		group&
			push_back(Param1&& param1, Param2&& param2, Params&&... params)
		{
			children_.reserve(children_.size() + 2 + sizeof...(params));
			push_back(std::forward<Param1>(param1));
			push_back(std::forward<Param2>(param2), std::forward<Params>(params)...);
			return *this;
		}


		//---------------------------------------------------------------
		/** @brief adds child parameter at the beginning */
		group&
			push_front(const parameter& v) {
			children_.emplace(children_.begin(), v);
			return *this;
		}
		//-----------------------------------------------------
		/** @brief adds child parameter at the beginning */
		group&
			push_front(parameter&& v) {
			children_.emplace(children_.begin(), std::move(v));
			return *this;
		}
		//-----------------------------------------------------
		/** @brief adds child group at the beginning */
		group&
			push_front(const group& g) {
			children_.emplace(children_.begin(), g);
			return *this;
		}
		//-----------------------------------------------------
		/** @brief adds child group at the beginning */
		group&
			push_front(group&& g) {
			children_.emplace(children_.begin(), std::move(g));
			return *this;
		}


		//---------------------------------------------------------------
		/** @brief adds all children of other group at the end */
		group&
			merge(group&& g)
		{
			children_.insert(children_.end(),
				std::make_move_iterator(g.begin()),
				std::make_move_iterator(g.end()));
			return *this;
		}
		//-----------------------------------------------------
		/** @brief adds all children of several other groups at the end */
		template<class... Groups>
		group&
			merge(group&& g1, group&& g2, Groups&&... gs)
		{
			merge(std::move(g1));
			merge(std::move(g2), std::forward<Groups>(gs)...);
			return *this;
		}


		//---------------------------------------------------------------
		/** @brief indexed, nutable access to child */
		child& operator [] (size_type index) noexcept {
			return children_[index];
		}
		/** @brief indexed, non-nutable access to child */
		const child& operator [] (size_type index) const noexcept {
			return children_[index];
		}

		//---------------------------------------------------------------
		/** @brief mutable access to first child */
		child& front()       noexcept { return children_.front(); }
		/** @brief non-mutable access to first child */
		const child& front() const noexcept { return children_.front(); }
		//-----------------------------------------------------
		/** @brief mutable access to last child */
		child& back()       noexcept { return children_.back(); }
		/** @brief non-mutable access to last child */
		const child& back() const noexcept { return children_.back(); }


		//---------------------------------------------------------------
		/** @brief returns true, if group has no children, false otherwise */
		bool empty() const noexcept { return children_.empty(); }

		/** @brief returns number of children */
		size_type size() const noexcept { return children_.size(); }

		/** @brief returns number of nested levels; 1 for a flat group */
		size_type depth() const {
			size_type n = 0;
			for (const auto& c : children_) {
				auto l = 1 + c.depth();
				if (l > n) n = l;
			}
			return n;
		}


		//---------------------------------------------------------------
		/** @brief returns mutating iterator to position of first element */
		iterator  begin()       noexcept { return children_.begin(); }
		/** @brief returns non-mutating iterator to position of first element */
		const_iterator  begin() const noexcept { return children_.begin(); }
		/** @brief returns non-mutating iterator to position of first element */
		const_iterator cbegin() const noexcept { return children_.begin(); }

		/** @brief returns mutating iterator to position one past the last element */
		iterator  end()         noexcept { return children_.end(); }
		/** @brief returns non-mutating iterator to position one past the last element */
		const_iterator  end()   const noexcept { return children_.end(); }
		/** @brief returns non-mutating iterator to position one past the last element */
		const_iterator cend()   const noexcept { return children_.end(); }


		//---------------------------------------------------------------
		/** @brief returns augmented iterator for depth first searches
		 *  @details traverser knows end of iteration and can skip over children
		 */
		depth_first_traverser
			begin_dfs() const noexcept {
			return depth_first_traverser{ *this };
		}


		//---------------------------------------------------------------
		/** @brief returns recursive parameter count */
		size_type param_count() const {
			size_type c = 0;
			for (const auto& n : children_) {
				c += n.param_count();
			}
			return c;
		}


		//---------------------------------------------------------------
		/** @brief returns range of all flags (recursive) */
		arg_list all_flags() const
		{
			std::vector<arg_string> all;
			gather_flags(children_, all);
			return all;
		}

		/** @brief returns true, if no flag occurs as true
		 *         prefix of any other flag (identical flags will be ignored) */
		bool flags_are_prefix_free() const
		{
			const auto fs = all_flags();

			using std::begin; using std::end;
			for (auto i = begin(fs), e = end(fs); i != e; ++i) {
				if (!i->empty()) {
					for (auto j = i + 1; j != e; ++j) {
						if (!j->empty() && *i != *j) {
							if (i->find(*j) == 0) return false;
							if (j->find(*i) == 0) return false;
						}
					}
				}
			}

			return true;
		}


		//---------------------------------------------------------------
		/** @brief returns longest common prefix of all flags */
		arg_string common_flag_prefix() const
		{
			arg_list prefixes;
			gather_prefixes(children_, prefixes);
			return str::longest_common_prefix(prefixes);
		}


	private:
		//---------------------------------------------------------------
		static void
			gather_flags(const children_store& nodes, arg_list& all)
		{
			for (const auto& p : nodes) {
				if (p.is_group()) {
					gather_flags(p.as_group().children_, all);
				}
				else {
					const auto& pf = p.as_param().flags();
					using std::begin;
					using std::end;
					if (!pf.empty()) all.insert(end(all), begin(pf), end(pf));
				}
			}
		}
		//---------------------------------------------------------------
		static void
			gather_prefixes(const children_store& nodes, arg_list& all)
		{
			for (const auto& p : nodes) {
				if (p.is_group()) {
					gather_prefixes(p.as_group().children_, all);
				}
				else if (!p.as_param().flags().empty()) {
					auto pfx = str::longest_common_prefix(p.as_param().flags());
					if (!pfx.empty()) all.push_back(std::move(pfx));
				}
			}
		}

		//---------------------------------------------------------------
		children_store children_;
		bool exclusive_ = false;
		bool joinable_ = false;
		bool scoped_ = false;
	};



	/*************************************************************************//**
	 *
	 * @brief group or parameter
	 *
	 *****************************************************************************/
	using pattern = group::child;



	/*************************************************************************//**
	 *
	 * @brief apply an action to all parameters in a group
	 *
	 *****************************************************************************/
	template<class Action>
	void for_all_params(group& g, Action&& action)
	{
		for (auto& p : g) {
			if (p.is_group()) {
				for_all_params(p.as_group(), action);
			}
			else {
				action(p.as_param());
			}
		}
	}

	template<class Action>
	void for_all_params(const group& g, Action&& action)
	{
		for (auto& p : g) {
			if (p.is_group()) {
				for_all_params(p.as_group(), action);
			}
			else {
				action(p.as_param());
			}
		}
	}



	/*************************************************************************//**
	 *
	 * @brief makes a group of parameters and/or groups
	 *
	 *****************************************************************************/
	inline group
		operator , (parameter a, parameter b)
	{
		return group{ std::move(a), std::move(b) }.scoped(false);
	}

	//---------------------------------------------------------
	inline group
		operator , (parameter a, group b)
	{
		return !b.scoped() && !b.blocking() && !b.exclusive() && !b.repeatable()
			&& !b.joinable() && (b.doc().empty() || b.doc() == a.doc())
			? b.push_front(std::move(a))
			: group{ std::move(a), std::move(b) }.scoped(false);
	}

	//---------------------------------------------------------
	inline group
		operator , (group a, parameter b)
	{
		return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
			&& !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
			? a.push_back(std::move(b))
			: group{ std::move(a), std::move(b) }.scoped(false);
	}

	//---------------------------------------------------------
	inline group
		operator , (group a, group b)
	{
		return !a.scoped() && !a.blocking() && !a.exclusive() && !a.repeatable()
			&& !a.joinable() && (a.doc().empty() || a.doc() == b.doc())
			? a.push_back(std::move(b))
			: group{ std::move(a), std::move(b) }.scoped(false);
	}



	/*************************************************************************//**
	 *
	 * @brief makes a group of alternative parameters or groups
	 *
	 *****************************************************************************/
	template<class Param, class... Params>
	inline group
		one_of(Param param, Params... params)
	{
		return group{ std::move(param), std::move(params)... }.exclusive(true);
	}


	/*************************************************************************//**
	 *
	 * @brief makes a group of alternative parameters or groups
	 *
	 *****************************************************************************/
	inline group
		operator | (parameter a, parameter b)
	{
		return group{ std::move(a), std::move(b) }.scoped(false).exclusive(true);
	}

	//-------------------------------------------------------------------
	inline group
		operator | (parameter a, group b)
	{
		return !b.scoped() && !b.blocking() && b.exclusive() && !b.repeatable()
			&& !b.joinable()
			&& (b.doc().empty() || b.doc() == a.doc())
			? b.push_front(std::move(a))
			: group{ std::move(a), std::move(b) }.scoped(false).exclusive(true);
	}

	//-------------------------------------------------------------------
	inline group
		operator | (group a, parameter b)
	{
		return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable()
			&& a.blocking() == b.blocking()
			&& (a.doc().empty() || a.doc() == b.doc())
			? a.push_back(std::move(b))
			: group{ std::move(a), std::move(b) }.scoped(false).exclusive(true);
	}

	inline group
		operator | (group a, group b)
	{
		return !a.scoped() && a.exclusive() && !a.repeatable() && !a.joinable()
			&& a.blocking() == b.blocking()
			&& (a.doc().empty() || a.doc() == b.doc())
			? a.push_back(std::move(b))
			: group{ std::move(a), std::move(b) }.scoped(false).exclusive(true);
	}



	/*************************************************************************//**
	 *
	 * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
	 *        no interface guarantees; might be changed or removed in the future
	 *
	 *****************************************************************************/
	namespace detail {

		inline void set_blocking(bool) {}

		template<class P, class... Ps>
		void set_blocking(bool yes, P& p, Ps&... ps) {
			p.blocking(yes);
			set_blocking(yes, ps...);
		}

	} // namespace detail


	/*************************************************************************//**
	 *
	 * @brief makes a parameter/group sequence by making all input objects blocking
	 *
	 *****************************************************************************/
	template<class Param, class... Params>
	inline group
		in_sequence(Param param, Params... params)
	{
		detail::set_blocking(true, param, params...);
		return group{ std::move(param), std::move(params)... }.scoped(true);
	}


	/*************************************************************************//**
	 *
	 * @brief makes a parameter/group sequence by making all input objects blocking
	 *
	 *****************************************************************************/
	inline group
		operator & (parameter a, parameter b)
	{
		a.blocking(true);
		b.blocking(true);
		return group{ std::move(a), std::move(b) }.scoped(true);
	}

	//---------------------------------------------------------
	inline group
		operator & (parameter a, group b)
	{
		a.blocking(true);
		return group{ std::move(a), std::move(b) }.scoped(true);
	}

	//---------------------------------------------------------
	inline group
		operator & (group a, parameter b)
	{
		b.blocking(true);
		if (a.all_blocking() && !a.exclusive() && !a.repeatable() && !a.joinable()
			&& (a.doc().empty() || a.doc() == b.doc()))
		{
			return a.push_back(std::move(b));
		}
		else {
			if (!a.all_blocking()) a.blocking(true);
			return group{ std::move(a), std::move(b) }.scoped(true);
		}
	}

	inline group
		operator & (group a, group b)
	{
		if (!b.all_blocking()) b.blocking(true);
		if (a.all_blocking() && !a.exclusive() && !a.repeatable()
			&& !a.joinable() && (a.doc().empty() || a.doc() == b.doc()))
		{
			return a.push_back(std::move(b));
		}
		else {
			if (!a.all_blocking()) a.blocking(true);
			return group{ std::move(a), std::move(b) }.scoped(true);
		}
	}



	/*************************************************************************//**
	 *
	 * @brief makes a group of parameters and/or groups
	 *        where all single char flag params ("-a", "b", ...) are joinable
	 *
	 *****************************************************************************/
	inline group
		joinable(group g) {
		return g.joinable(true);
	}

	//-------------------------------------------------------------------
	template<class... Params>
	inline group
		joinable(parameter param, Params... params)
	{
		return group{ std::move(param), std::move(params)... }.joinable(true);
	}

	template<class P2, class... Ps>
	inline group
		joinable(group p1, P2 p2, Ps... ps)
	{
		return group{ std::move(p1), std::move(p2), std::move(ps)... }.joinable(true);
	}

	template<class Param, class... Params>
	inline group
		joinable(doc_string docstr, Param param, Params... params)
	{
		return group{ std::move(param), std::move(params)... }
		.joinable(true).doc(std::move(docstr));
	}



	/*************************************************************************//**
	 *
	 * @brief makes a repeatable copy of a parameter
	 *
	 *****************************************************************************/
	inline parameter
		repeatable(parameter p) {
		return p.repeatable(true);
	}

	/*************************************************************************//**
	 *
	 * @brief makes a repeatable copy of a group
	 *
	 *****************************************************************************/
	inline group
		repeatable(group g) {
		return g.repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes a group of parameters and/or groups
	 *        that is repeatable as a whole
	 *        Note that a repeatable group consisting entirely of non-blocking
	 *        children is equivalent to a non-repeatable group of
	 *        repeatable children.
	 *
	 *****************************************************************************/
	template<class P2, class... Ps>
	inline group
		repeatable(parameter p1, P2 p2, Ps... ps)
	{
		return group{ std::move(p1), std::move(p2),
					 std::move(ps)... }.repeatable(true);
	}

	template<class P2, class... Ps>
	inline group
		repeatable(group p1, P2 p2, Ps... ps)
	{
		return group{ std::move(p1), std::move(p2),
					 std::move(ps)... }.repeatable(true);
	}



	/*************************************************************************//**
	 *
	 * @brief makes a parameter greedy (match with top priority)
	 *
	 *****************************************************************************/
	inline parameter
		greedy(parameter p) {
		return p.greedy(true);
	}

	inline parameter
		operator ! (parameter p) {
		return greedy(p);
	}



	/*************************************************************************//**
	 *
	 * @brief recursively prepends a prefix to all flags
	 *
	 *****************************************************************************/
	inline parameter&&
		with_prefix(const arg_string& prefix, parameter&& p) {
		return std::move(with_prefix(prefix, p));
	}


	//-------------------------------------------------------------------
	inline group&
		with_prefix(const arg_string& prefix, group& g)
	{
		for (auto& p : g) {
			if (p.is_group()) {
				with_prefix(prefix, p.as_group());
			}
			else {
				with_prefix(prefix, p.as_param());
			}
		}
		return g;
	}


	inline group&&
		with_prefix(const arg_string& prefix, group&& params)
	{
		return std::move(with_prefix(prefix, params));
	}


	template<class Param, class... Params>
	inline group
		with_prefix(arg_string prefix, Param&& param, Params&&... params)
	{
		return with_prefix(prefix, group{ std::forward<Param>(param),
										 std::forward<Params>(params)... });
	}



	/*************************************************************************//**
	 *
	 * @brief recursively prepends a prefix to all flags
	 *
	 * @param shortpfx : used for single-letter flags
	 * @param longpfx  : used for flags with length > 1
	 *
	 *****************************************************************************/
	inline parameter&&
		with_prefixes_short_long(const arg_string& shortpfx, const arg_string& longpfx,
			parameter&& p)
	{
		return std::move(with_prefixes_short_long(shortpfx, longpfx, p));
	}


	//-------------------------------------------------------------------
	inline group&
		with_prefixes_short_long(const arg_string& shortFlagPrefix,
			const arg_string& longFlagPrefix,
			group& g)
	{
		for (auto& p : g) {
			if (p.is_group()) {
				with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_group());
			}
			else {
				with_prefixes_short_long(shortFlagPrefix, longFlagPrefix, p.as_param());
			}
		}
		return g;
	}


	inline group&&
		with_prefixes_short_long(const arg_string& shortFlagPrefix,
			const arg_string& longFlagPrefix,
			group&& params)
	{
		return std::move(with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
			params));
	}


	template<class Param, class... Params>
	inline group
		with_prefixes_short_long(const arg_string& shortFlagPrefix,
			const arg_string& longFlagPrefix,
			Param&& param, Params&&... params)
	{
		return with_prefixes_short_long(shortFlagPrefix, longFlagPrefix,
			group{ std::forward<Param>(param),
				  std::forward<Params>(params)... });
	}



	/*************************************************************************//**
	 *
	 * @brief recursively prepends a suffix to all flags
	 *
	 *****************************************************************************/
	inline parameter&&
		with_suffix(const arg_string& suffix, parameter&& p) {
		return std::move(with_suffix(suffix, p));
	}


	//-------------------------------------------------------------------
	inline group&
		with_suffix(const arg_string& suffix, group& g)
	{
		for (auto& p : g) {
			if (p.is_group()) {
				with_suffix(suffix, p.as_group());
			}
			else {
				with_suffix(suffix, p.as_param());
			}
		}
		return g;
	}


	inline group&&
		with_suffix(const arg_string& suffix, group&& params)
	{
		return std::move(with_suffix(suffix, params));
	}


	template<class Param, class... Params>
	inline group
		with_suffix(arg_string suffix, Param&& param, Params&&... params)
	{
		return with_suffix(suffix, group{ std::forward<Param>(param),
										 std::forward<Params>(params)... });
	}



	/*************************************************************************//**
	 *
	 * @brief recursively prepends a suffix to all flags
	 *
	 * @param shortsfx : used for single-letter flags
	 * @param longsfx  : used for flags with length > 1
	 *
	 *****************************************************************************/
	inline parameter&&
		with_suffixes_short_long(const arg_string& shortsfx, const arg_string& longsfx,
			parameter&& p)
	{
		return std::move(with_suffixes_short_long(shortsfx, longsfx, p));
	}


	//-------------------------------------------------------------------
	inline group&
		with_suffixes_short_long(const arg_string& shortFlagSuffix,
			const arg_string& longFlagSuffix,
			group& g)
	{
		for (auto& p : g) {
			if (p.is_group()) {
				with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_group());
			}
			else {
				with_suffixes_short_long(shortFlagSuffix, longFlagSuffix, p.as_param());
			}
		}
		return g;
	}


	inline group&&
		with_suffixes_short_long(const arg_string& shortFlagSuffix,
			const arg_string& longFlagSuffix,
			group&& params)
	{
		return std::move(with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
			params));
	}


	template<class Param, class... Params>
	inline group
		with_suffixes_short_long(const arg_string& shortFlagSuffix,
			const arg_string& longFlagSuffix,
			Param&& param, Params&&... params)
	{
		return with_suffixes_short_long(shortFlagSuffix, longFlagSuffix,
			group{ std::forward<Param>(param),
				  std::forward<Params>(params)... });
	}








	/*************************************************************************//**
	 *
	 * @brief parsing implementation details
	 *
	 *****************************************************************************/

	namespace detail {


		/*************************************************************************//**
		 *
		 * @brief DFS traverser that keeps track of 'scopes'
		 *        scope = all parameters that are either bounded by
		 *        two blocking parameters on the same depth level
		 *        or the beginning/end of the outermost group
		 *
		 *****************************************************************************/
		class scoped_dfs_traverser
		{
		public:
			using dfs_traverser = group::depth_first_traverser;

			scoped_dfs_traverser() = default;

			explicit
				scoped_dfs_traverser(const group& g) :
				pos_{ g }, lastMatch_{}, posAfterLastMatch_{}, scopes_{},
				ignoreBlocks_{ false },
				repeatGroupStarted_{ false }, repeatGroupContinues_{ false }
			{}

			const dfs_traverser& base() const noexcept { return pos_; }
			const dfs_traverser& last_match() const noexcept { return lastMatch_; }

			const group& parent() const noexcept { return pos_.parent(); }

			const group* innermost_repeat_group() const noexcept {
				return pos_.innermost_repeat_group();
			}
			const group* outermost_join_group() const noexcept {
				return pos_.outermost_join_group();
			}
			const group* innermost_blocking_group() const noexcept {
				return pos_.innermost_blocking_group();
			}
			const group* innermost_exclusive_group() const noexcept {
				return pos_.innermost_exclusive_group();
			}

			const pattern* operator ->() const noexcept { return pos_.operator->(); }
			const pattern& operator *() const noexcept { return *pos_; }

			const pattern* ptr() const noexcept { return pos_.operator->(); }

			explicit operator bool() const noexcept { return bool(pos_); }

			bool joinable() const noexcept { return pos_.joinable(); }
			arg_string common_flag_prefix() const { return pos_.common_flag_prefix(); }

			void ignore_blocking(bool yes) { ignoreBlocks_ = yes; }

			void invalidate() {
				pos_.invalidate();
			}

			bool matched() const noexcept {
				return (pos_ == lastMatch_);
			}

			bool start_of_repeat_group() const noexcept { return repeatGroupStarted_; }

			//-----------------------------------------------------
			scoped_dfs_traverser&
				next_sibling() { pos_.next_sibling(); return *this; }

			scoped_dfs_traverser&
				next_after_siblings() { pos_.next_after_siblings(); return *this; }


			//-----------------------------------------------------
			scoped_dfs_traverser&
				operator ++ ()
			{
				if (!pos_) return *this;

				if (pos_.is_last_in_path()) {
					return_to_outermost_scope();
					return *this;
				}

				//current pattern can block if it didn't match already
				if (ignoreBlocks_ || matched()) {
					++pos_;
				}
				else if (!pos_->is_group()) {
					//current group can block if we didn't have any match in it
					const group* g = pos_.outermost_blocking_group_fully_explored();
					//no match in 'g' before -> skip to after its siblings
					if (g && !lastMatch_.is_inside(g)) {
						pos_.back_to_ancestor(g).next_after_siblings();
						if (!pos_) return_to_outermost_scope();
					}
					else if (pos_->blocking()) {
						if (pos_.parent().exclusive()) {
							pos_.next_sibling();
						}
						else {
							//no match => skip siblings of blocking param
							pos_.next_after_siblings();
						}
						if (!pos_) return_to_outermost_scope();
					}
					else {
						++pos_;
					}
				}
				else {
					++pos_;
				}
				check_if_left_scope();
				return *this;
			}

			//-----------------------------------------------------
			void next_after_match(scoped_dfs_traverser match)
			{
				if (!match || ignoreBlocks_) return;

				check_repeat_group_start(match);

				lastMatch_ = match.base();

				// if there is a blocking ancestor -> go back to it
				if (!match->blocking()) {
					match.pos_.back_to_ancestor(match.innermost_blocking_group());
				}

				//if match is not in current position & current position is blocking
				//=> current position has to be advanced by one so that it is
				//no longer reachable within current scope
				//(can happen for repeatable, blocking parameters)
				if (match.base() != pos_ && pos_->blocking()) pos_.next_sibling();

				if (match->blocking()) {
					if (match.pos_.is_alternative()) {
						//discard other alternatives
						match.pos_.skip_alternatives();
					}

					if (is_last_in_current_scope(match.pos_)) {
						//if current param is not repeatable -> back to previous scope
						if (!match->repeatable() && !match->is_group()) {
							pos_ = std::move(match.pos_);
							if (!scopes_.empty()) pos_.undo(scopes_.top());
						}
						else { //stay at match position
							pos_ = std::move(match.pos_);
						}
					}
					else { //not last in current group
						//if current param is not repeatable, go directly to next
						if (!match->repeatable() && !match->is_group()) {
							++match.pos_;
						}

						if (match.pos_.level() > pos_.level()) {
							scopes_.push(pos_.undo_point());
							pos_ = std::move(match.pos_);
						}
						else if (match.pos_.level() < pos_.level()) {
							return_to_level(match.pos_.level());
						}
						else {
							pos_ = std::move(match.pos_);
						}
					}
					posAfterLastMatch_ = pos_;
				}
				else {
					if (match.pos_.level() < pos_.level()) {
						return_to_level(match.pos_.level());
					}
					posAfterLastMatch_ = pos_;
				}
				repeatGroupContinues_ = repeat_group_continues();
			}

		private:
			//-----------------------------------------------------
			bool is_last_in_current_scope(const dfs_traverser& pos) const
			{
				if (scopes_.empty()) return pos.is_last_in_path();
				//check if we would leave the current scope on ++
				auto p = pos;
				++p;
				return p.level() < scopes_.top().level();
			}

			//-----------------------------------------------------
			void check_repeat_group_start(const scoped_dfs_traverser& newMatch)
			{
				const auto newrg = newMatch.innermost_repeat_group();
				if (!newrg) {
					repeatGroupStarted_ = false;
				}
				else if (lastMatch_.innermost_repeat_group() != newrg) {
					repeatGroupStarted_ = true;
				}
				else if (!repeatGroupContinues_ || !newMatch.repeatGroupContinues_) {
					repeatGroupStarted_ = true;
				}
				else {
					//special case: repeat group is outermost group
					//=> we can never really 'leave' and 'reenter' it
					//but if the current scope is the first element, then we are
					//conceptually at a position 'before' the group
					repeatGroupStarted_ = scopes_.empty() || (
						newrg == pos_.root() &&
						scopes_.top().param() == &(*pos_.root()->begin()));
				}
				repeatGroupContinues_ = repeatGroupStarted_;
			}

			//-----------------------------------------------------
			bool repeat_group_continues() const
			{
				if (!repeatGroupContinues_) return false;
				const auto curRepGroup = pos_.innermost_repeat_group();
				if (!curRepGroup) return false;
				if (curRepGroup != lastMatch_.innermost_repeat_group()) return false;
				if (!posAfterLastMatch_) return false;
				return true;
			}

			//-----------------------------------------------------
			void check_if_left_scope()
			{
				if (posAfterLastMatch_) {
					if (pos_.level() < posAfterLastMatch_.level()) {
						while (!scopes_.empty() && scopes_.top().level() >= pos_.level()) {
							pos_.undo(scopes_.top());
							scopes_.pop();
						}
						posAfterLastMatch_.invalidate();
					}
				}
				while (!scopes_.empty() && scopes_.top().level() > pos_.level()) {
					pos_.undo(scopes_.top());
					scopes_.pop();
				}
				repeatGroupContinues_ = repeat_group_continues();
			}

			//-----------------------------------------------------
			void return_to_outermost_scope()
			{
				posAfterLastMatch_.invalidate();

				if (scopes_.empty()) {
					pos_.invalidate();
					repeatGroupContinues_ = false;
					return;
				}

				while (!scopes_.empty() && (!pos_ || pos_.level() >= 1)) {
					pos_.undo(scopes_.top());
					scopes_.pop();
				}
				while (!scopes_.empty()) scopes_.pop();

				repeatGroupContinues_ = repeat_group_continues();
			}

			//-----------------------------------------------------
			void return_to_level(int level)
			{
				if (pos_.level() <= level) return;
				while (!scopes_.empty() && pos_.level() > level) {
					pos_.undo(scopes_.top());
					scopes_.pop();
				}
			};

			dfs_traverser pos_;
			dfs_traverser lastMatch_;
			dfs_traverser posAfterLastMatch_;
			std::stack<dfs_traverser::memento> scopes_;
			bool ignoreBlocks_ = false;
			bool repeatGroupStarted_ = false;
			bool repeatGroupContinues_ = false;
		};




		/*****************************************************************************
		 *
		 * some parameter property predicates
		 *
		 *****************************************************************************/
		struct select_all {
			bool operator () (const parameter&) const noexcept { return true; }
		};

		struct select_flags {
			bool operator () (const parameter& p) const noexcept {
				return !p.flags().empty();
			}
		};

		struct select_values {
			bool operator () (const parameter& p) const noexcept {
				return p.flags().empty();
			}
		};



		/*************************************************************************//**
		 *
		 * @brief result of a matching operation
		 *
		 *****************************************************************************/
		class match_t {
		public:
			using size_type = arg_string::size_type;

			match_t() = default;

			match_t(arg_string s, scoped_dfs_traverser p) :
				str_{ std::move(s) }, pos_{ std::move(p) }
			{}

			size_type length() const noexcept { return str_.size(); }

			const arg_string& str() const noexcept { return str_; }
			const scoped_dfs_traverser& pos() const noexcept { return pos_; }

			explicit operator bool() const noexcept { return bool(pos_); }

		private:
			arg_string str_;
			scoped_dfs_traverser pos_;
		};



		/*************************************************************************//**
		 *
		 * @brief finds the first parameter that matches a given string;
		 *        candidate parameters are traversed using a scoped DFS traverser
		 *
		 *****************************************************************************/
		template<class ParamSelector>
		match_t
			full_match(scoped_dfs_traverser pos, const arg_string& arg,
				const ParamSelector& select)
		{
			while (pos) {
				if (pos->is_param()) {
					const auto& param = pos->as_param();
					if (select(param)) {
						const auto match = param.match(arg);
						if (match && match.length() == arg.size()) {
							return match_t{ arg, std::move(pos) };
						}
					}
				}
				++pos;
			}
			return match_t{};
		}



		/*************************************************************************//**
		 *
		 * @brief finds the first parameter that matches any (non-empty) prefix
		 *        of a given string;
		 *        candidate parameters are traversed using a scoped DFS traverser
		 *
		 *****************************************************************************/
		template<class ParamSelector>
		match_t
			longest_prefix_match(scoped_dfs_traverser pos, const arg_string& arg,
				const ParamSelector& select)
		{
			match_t longest;

			while (pos) {
				if (pos->is_param()) {
					const auto& param = pos->as_param();
					if (select(param)) {
						auto match = param.match(arg);
						if (match.prefix()) {
							if (match.length() == arg.size()) {
								return match_t{ arg, std::move(pos) };
							}
							else if (match.length() > longest.length()) {
								longest = match_t{ arg.substr(match.at(), match.length()),
												  pos };
							}
						}
					}
				}
				++pos;
			}
			return longest;
		}



		/*************************************************************************//**
		 *
		 * @brief finds the first parameter that partially matches a given string;
		 *        candidate parameters are traversed using a scoped DFS traverser
		 *
		 *****************************************************************************/
		template<class ParamSelector>
		match_t
			partial_match(scoped_dfs_traverser pos, const arg_string& arg,
				const ParamSelector& select)
		{
			while (pos) {
				if (pos->is_param()) {
					const auto& param = pos->as_param();
					if (select(param)) {
						const auto match = param.match(arg);
						if (match) {
							return match_t{ arg.substr(match.at(), match.length()),
										   std::move(pos) };
						}
					}
				}
				++pos;
			}
			return match_t{};
		}

	} //namespace detail






	/***************************************************************//**
	 *
	 * @brief default command line arguments parser
	 *
	 *******************************************************************/
	class parser
	{
	public:
		using dfs_traverser = group::depth_first_traverser;
		using scoped_dfs_traverser = detail::scoped_dfs_traverser;


		/*****************************************************//**
		 * @brief arg -> parameter mapping
		 *********************************************************/
		class arg_mapping {
		public:
			friend class parser;

			explicit
				arg_mapping(arg_index idx, arg_string s,
					const dfs_traverser& match)
				:
				index_{ idx }, arg_{ std::move(s) }, match_{ match },
				repeat_{ 0 }, startsRepeatGroup_{ false },
				blocked_{ false }, conflict_{ false }
			{}

			explicit
				arg_mapping(arg_index idx, arg_string s) :
				index_{ idx }, arg_{ std::move(s) }, match_{},
				repeat_{ 0 }, startsRepeatGroup_{ false },
				blocked_{ false }, conflict_{ false }
			{}

			arg_index index() const noexcept { return index_; }
			const arg_string& arg() const noexcept { return arg_; }

			const parameter* param() const noexcept {
				return match_ && match_->is_param()
					? &(match_->as_param()) : nullptr;
			}

			std::size_t repeat() const noexcept { return repeat_; }

			bool blocked() const noexcept { return blocked_; }
			bool conflict() const noexcept { return conflict_; }

			bool bad_repeat() const noexcept {
				if (!param()) return false;
				return repeat_ > 0 && !param()->repeatable()
					&& !match_.innermost_repeat_group();
			}

			bool any_error() const noexcept {
				return !match_ || blocked() || conflict() || bad_repeat();
			}

		private:
			arg_index index_;
			arg_string arg_;
			dfs_traverser match_;
			std::size_t repeat_;
			bool startsRepeatGroup_;
			bool blocked_;
			bool conflict_;
		};

		/*****************************************************//**
		 * @brief references a non-matched, required parameter
		 *********************************************************/
		class missing_event {
		public:
			explicit
				missing_event(const parameter* p, arg_index after) :
				param_{ p }, aftIndex_{ after }
			{}

			const parameter* param() const noexcept { return param_; }

			arg_index after_index() const noexcept { return aftIndex_; }

		private:
			const parameter* param_;
			arg_index aftIndex_;
		};

		//-----------------------------------------------------
		using missing_events = std::vector<missing_event>;
		using arg_mappings = std::vector<arg_mapping>;


	private:
		struct miss_candidate {
			miss_candidate(dfs_traverser p, arg_index idx,
				bool firstInRepeatGroup = false) :
				pos{ std::move(p) }, index{ idx },
				startsRepeatGroup{ firstInRepeatGroup }
			{}

			dfs_traverser pos;
			arg_index index;
			bool startsRepeatGroup;
		};
		using miss_candidates = std::vector<miss_candidate>;


	public:
		//---------------------------------------------------------------
		/** @brief initializes parser with a command line interface
		 *  @param offset = argument index offset used for reports
		 * */
		explicit
			parser(const group& root, arg_index offset = 0) :
			root_{ &root }, pos_{ root },
			index_{ offset - 1 }, eaten_{ 0 },
			args_{}, missCand_{}, blocked_{ false }
		{
			for_each_potential_miss(dfs_traverser{ root },
				[this](const dfs_traverser& p) {
				missCand_.emplace_back(p, index_);
			});
		}


		//---------------------------------------------------------------
		/** @brief processes one command line argument */
		bool operator() (const arg_string& arg)
		{
			++eaten_;
			++index_;

			if (!valid()) return false;

			if (!blocked_ && try_match(arg)) return true;

			if (try_match_blocked(arg)) return false;

			//skipping of blocking & required patterns is not allowed
			if (!blocked_ && !pos_.matched() && pos_->required() && pos_->blocking()) {
				blocked_ = true;
			}

			add_nomatch(arg);
			return false;
		}


		//---------------------------------------------------------------
		/** @brief returns range of argument -> parameter mappings */
		const arg_mappings& args() const {
			return args_;
		}

		/** @brief returns list of missing events */
		missing_events missed() const {
			missing_events misses;
			misses.reserve(missCand_.size());
			for (auto i = missCand_.begin(); i != missCand_.end(); ++i) {
				misses.emplace_back(&(i->pos->as_param()), i->index);
			}
			return misses;
		}

		/** @brief returns number of processed command line arguments */
		arg_index parse_count() const noexcept { return eaten_; }

		/** @brief returns false if previously processed command line arguments
		 *         lead to an invalid / inconsistent parsing result
		 */
		bool valid() const noexcept { return bool(pos_); }

		/** @brief returns false if previously processed command line arguments
		 *         lead to an invalid / inconsistent parsing result
		 */
		explicit operator bool() const noexcept { return valid(); }


	private:
		//---------------------------------------------------------------
		using match_t = detail::match_t;


		//---------------------------------------------------------------
		/** @brief try to match argument with unreachable parameter */
		bool try_match_blocked(const arg_string& arg)
		{
			//try to match ahead (using temporary parser)
			if (pos_) {
				auto ahead = *this;
				if (try_match_blocked(std::move(ahead), arg)) return true;
			}

			//try to match from the beginning (using temporary parser)
			if (root_) {
				parser all{ *root_, index_ + 1 };
				if (try_match_blocked(std::move(all), arg)) return true;
			}

			return false;
		}

		//---------------------------------------------------------------
		bool try_match_blocked(parser&& parse, const arg_string& arg)
		{
			const auto nold = int(parse.args_.size());

			parse.pos_.ignore_blocking(true);

			if (!parse.try_match(arg)) return false;

			for (auto i = parse.args_.begin() + nold; i != parse.args_.end(); ++i) {
				args_.push_back(*i);
				args_.back().blocked_ = true;
			}
			return true;
		}

		//---------------------------------------------------------------
		/** @brief try to find a parameter/pattern that matches 'arg' */
		bool try_match(const arg_string& arg)
		{
			//match greedy parameters before everything else
			if (pos_->is_param() && pos_->blocking() && pos_->as_param().greedy()) {
				const auto match = pos_->as_param().match(arg);
				if (match && match.length() == arg.size()) {
					add_match(detail::match_t{ arg,pos_ });
					return true;
				}
			}

			//try flags first (alone, joinable or strict sequence)
			if (try_match_full(arg, detail::select_flags{})) return true;
			if (try_match_joined_flags(arg)) return true;
			if (try_match_joined_sequence(arg, detail::select_flags{})) return true;
			//try value params (alone or strict sequence)
			if (try_match_full(arg, detail::select_values{})) return true;
			if (try_match_joined_sequence(arg, detail::select_all{})) return true;
			//try joinable params + values in any order
			if (try_match_joined_params(arg)) return true;
			return false;
		}

		//---------------------------------------------------------------
		/**
		 * @brief try to match full argument
		 * @param select : predicate that candidate parameters must satisfy
		 */
		template<class ParamSelector>
		bool try_match_full(const arg_string& arg, const ParamSelector& select)
		{
			auto match = detail::full_match(pos_, arg, select);
			if (!match) return false;
			add_match(match);
			return true;
		}

		//---------------------------------------------------------------
		/**
		 * @brief try to match argument as blocking sequence of parameters
		 * @param select : predicate that a parameter matching the prefix of
		 *                 'arg' must satisfy
		 */
		template<class ParamSelector>
		bool try_match_joined_sequence(arg_string arg,
			const ParamSelector& acceptFirst)
		{
			auto fstMatch = detail::longest_prefix_match(pos_, arg, acceptFirst);

			if (!fstMatch) return false;

			if (fstMatch.str().size() == arg.size()) {
				add_match(fstMatch);
				return true;
			}

			if (!fstMatch.pos()->blocking()) return false;

			auto pos = fstMatch.pos();
			pos.ignore_blocking(true);
			const auto parent = &pos.parent();
			if (!pos->repeatable()) ++pos;

			arg.erase(0, fstMatch.str().size());
			std::vector<match_t> matches{ std::move(fstMatch) };

			while (!arg.empty() && pos &&
				pos->blocking() && pos->is_param() &&
				(&pos.parent() == parent))
			{
				auto match = pos->as_param().match(arg);

				if (match.prefix()) {
					matches.emplace_back(arg.substr(0, match.length()), pos);
					arg.erase(0, match.length());
					if (!pos->repeatable()) ++pos;
				}
				else {
					if (!pos->repeatable()) return false;
					++pos;
				}

			}
			//if arg not fully covered => discard temporary matches
			if (!arg.empty() || matches.empty()) return false;

			for (const auto& m : matches) add_match(m);
			return true;
		}

		//-----------------------------------------------------
		/** @brief try to match 'arg' as a concatenation of joinable flags */
		bool try_match_joined_flags(const arg_string& arg)
		{
			return find_join_group(pos_, [&](const group& g) {
				return try_match_joined(g, arg, detail::select_flags{},
					g.common_flag_prefix());
			});
		}

		//---------------------------------------------------------------
		/** @brief try to match 'arg' as a concatenation of joinable parameters */
		bool try_match_joined_params(const arg_string& arg)
		{
			return find_join_group(pos_, [&](const group& g) {
				return try_match_joined(g, arg, detail::select_all{});
			});
		}

		//-----------------------------------------------------
		/** @brief try to match 'arg' as concatenation of joinable parameters
		 *         that are all contained within one group
		 */
		template<class ParamSelector>
		bool try_match_joined(const group& joinGroup, arg_string arg,
			const ParamSelector& select,
			const arg_string& prefix = "")
		{
			//temporary parser with 'joinGroup' as top-level group
			parser parse{ joinGroup };
			//records temporary matches
			std::vector<match_t> matches;

			while (!arg.empty()) {
				auto match = detail::longest_prefix_match(parse.pos_, arg, select);

				if (!match) return false;

				arg.erase(0, match.str().size());
				//make sure prefix is always present after the first match
				//so that, e.g., flags "-a" and "-b" will be found in "-ab"
				if (!arg.empty() && !prefix.empty() && arg.find(prefix) != 0 &&
					prefix != match.str())
				{
					arg.insert(0, prefix);
				}

				parse.add_match(match);
				matches.push_back(std::move(match));
			}

			if (!arg.empty() || matches.empty()) return false;

			if (!parse.missCand_.empty()) return false;
			for (const auto& a : parse.args_) if (a.any_error()) return false;

			//replay matches onto *this
			for (const auto& m : matches) add_match(m);
			return true;
		}

		//-----------------------------------------------------
		template<class GroupSelector>
		bool find_join_group(const scoped_dfs_traverser& start,
			const GroupSelector& accept) const
		{
			if (start && start.parent().joinable()) {
				const auto& g = start.parent();
				if (accept(g)) return true;
				return false;
			}

			auto pos = start;
			while (pos) {
				if (pos->is_group() && pos->as_group().joinable()) {
					const auto& g = pos->as_group();
					if (accept(g)) return true;
					pos.next_sibling();
				}
				else {
					++pos;
				}
			}
			return false;
		}


		//---------------------------------------------------------------
		void add_nomatch(const arg_string& arg) {
			args_.emplace_back(index_, arg);
		}


		//---------------------------------------------------------------
		void add_match(const match_t& match)
		{
			const auto& pos = match.pos();
			if (!pos || !pos->is_param()) return;

			pos_.next_after_match(pos);

			arg_mapping newArg{ index_, match.str(), pos.base() };
			newArg.repeat_ = occurrences_of(&pos->as_param());
			newArg.conflict_ = check_conflicts(pos.base());
			newArg.startsRepeatGroup_ = pos_.start_of_repeat_group();
			args_.push_back(std::move(newArg));

			add_miss_candidates_after(pos);
			clean_miss_candidates_for(pos.base());
			discard_alternative_miss_candidates(pos.base());

		}

		//-----------------------------------------------------
		bool check_conflicts(const dfs_traverser& match)
		{
			if (pos_.start_of_repeat_group()) return false;
			bool conflict = false;
			for (const auto& m : match.stack()) {
				if (m.parent->exclusive()) {
					for (auto i = args_.rbegin(); i != args_.rend(); ++i) {
						if (!i->blocked()) {
							for (const auto& c : i->match_.stack()) {
								//sibling within same exclusive group => conflict
								if (c.parent == m.parent && c.cur != m.cur) {
									conflict = true;
									i->conflict_ = true;
								}
							}
						}
						//check for conflicts only within current repeat cycle
						if (i->startsRepeatGroup_) break;
					}
				}
			}
			return conflict;
		}

		//-----------------------------------------------------
		void clean_miss_candidates_for(const dfs_traverser& match)
		{
			auto i = std::find_if(missCand_.rbegin(), missCand_.rend(),
				[&](const miss_candidate& m) {
				return &(*m.pos) == &(*match);
			});

			if (i != missCand_.rend()) {
				missCand_.erase(prev(i.base()));
			}
		}

		//-----------------------------------------------------
		void discard_alternative_miss_candidates(const dfs_traverser& match)
		{
			if (missCand_.empty()) return;
			//find out, if miss candidate is sibling of one of the same
			//alternative groups that the current match is a member of
			//if so, we can discard the miss

			//go through all exclusive groups of matching pattern
			for (const auto& m : match.stack()) {
				if (m.parent->exclusive()) {
					for (auto i = int(missCand_.size()) - 1; i >= 0; --i) {
						bool removed = false;
						for (const auto& c : missCand_[i].pos.stack()) {
							//sibling within same exclusive group => discard
							if (c.parent == m.parent && c.cur != m.cur) {
								missCand_.erase(missCand_.begin() + i);
								if (missCand_.empty()) return;
								removed = true;
								break;
							}
						}
						//remove miss candidates only within current repeat cycle
						if (i > 0 && removed) {
							if (missCand_[i - 1].startsRepeatGroup) break;
						}
						else {
							if (missCand_[i].startsRepeatGroup) break;
						}
					}
				}
			}
		}

		//-----------------------------------------------------
		void add_miss_candidates_after(const scoped_dfs_traverser& match)
		{
			auto npos = match.base();
			if (npos.is_alternative()) npos.skip_alternatives();
			++npos;
			//need to add potential misses if:
			//either new repeat group was started
			const auto newRepGroup = match.innermost_repeat_group();
			if (newRepGroup) {
				if (pos_.start_of_repeat_group()) {
					for_each_potential_miss(std::move(npos),
						[&, this](const dfs_traverser& pos) {
						//only add candidates within repeat group
						if (newRepGroup == pos.innermost_repeat_group()) {
							missCand_.emplace_back(pos, index_, true);
						}
					});
				}
			}
			//... or an optional blocking param was hit
			else if (match->blocking() && !match->required() &&
				npos.level() >= match.base().level())
			{
				for_each_potential_miss(std::move(npos),
					[&, this](const dfs_traverser& pos) {
					//only add new candidates
					if (std::find_if(missCand_.begin(), missCand_.end(),
						[&](const miss_candidate& c) {
						return &(*c.pos) == &(*pos);
					}) == missCand_.end())
					{
						missCand_.emplace_back(pos, index_);
					}
				});
			}

		}

		//-----------------------------------------------------
		template<class Action>
		static void
			for_each_potential_miss(dfs_traverser pos, Action&& action)
		{
			const auto level = pos.level();
			while (pos && pos.level() >= level) {
				if (pos->is_group()) {
					const auto& g = pos->as_group();
					if (g.all_optional() || (g.exclusive() && g.any_optional())) {
						pos.next_sibling();
					}
					else {
						++pos;
					}
				}
				else {  //param
					if (pos->required()) {
						action(pos);
						++pos;
					}
					else if (pos->blocking()) { //optional + blocking
						pos.next_after_siblings();
					}
					else {
						++pos;
					}
				}
			}
		}


		//---------------------------------------------------------------
		std::size_t occurrences_of(const parameter* p) const
		{
			if (!p) return 0;

			auto i = std::find_if(args_.rbegin(), args_.rend(),
				[p](const arg_mapping& a) { return a.param() == p; });

			if (i != args_.rend()) return i->repeat() + 1;
			return 0;
		}


		//---------------------------------------------------------------
		const group* root_;
		scoped_dfs_traverser pos_;
		arg_index index_;
		arg_index eaten_;
		arg_mappings args_;
		miss_candidates missCand_;
		bool blocked_;
	};




	/*************************************************************************//**
	 *
	 * @brief contains argument -> parameter mappings
	 *        and missing parameters
	 *
	 *****************************************************************************/
	class parsing_result
	{
	public:
		using arg_mapping = parser::arg_mapping;
		using arg_mappings = parser::arg_mappings;
		using missing_event = parser::missing_event;
		using missing_events = parser::missing_events;
		using iterator = arg_mappings::const_iterator;

		//-----------------------------------------------------
		/** @brief default: empty result */
		parsing_result() = default;

		parsing_result(arg_mappings arg2param, missing_events misses) :
			arg2param_{ std::move(arg2param) }, missing_{ std::move(misses) }
		{}

		//-----------------------------------------------------
		/** @brief returns number of arguments that could not be mapped to
		 *         a parameter
		 */
		arg_mappings::size_type
			unmapped_args_count() const noexcept {
			return std::count_if(arg2param_.begin(), arg2param_.end(),
				[](const arg_mapping& a) { return !a.param(); });
		}

		/** @brief returns if any argument could only be matched by an
		 *         unreachable parameter
		 */
		bool any_blocked() const noexcept {
			return std::any_of(arg2param_.begin(), arg2param_.end(),
				[](const arg_mapping& a) { return a.blocked(); });
		}

		/** @brief returns if any argument matched more than one parameter
		 *         that were mutually exclusive */
		bool any_conflict() const noexcept {
			return std::any_of(arg2param_.begin(), arg2param_.end(),
				[](const arg_mapping& a) { return a.conflict(); });
		}

		/** @brief returns if any parameter matched repeatedly although
		 *         it was not allowed to */
		bool any_bad_repeat() const noexcept {
			return std::any_of(arg2param_.begin(), arg2param_.end(),
				[](const arg_mapping& a) { return a.bad_repeat(); });
		}

		/** @brief returns true if any parsing error / violation of the
		 *         command line interface definition occurred */
		bool any_error() const noexcept {
			return unmapped_args_count() > 0 || !missing().empty() ||
				any_blocked() || any_conflict() || any_bad_repeat();
		}

		/** @brief returns true if no parsing error / violation of the
		 *         command line interface definition occurred */
		explicit operator bool() const noexcept { return !any_error(); }

		/** @brief access to range of missing parameter match events */
		const missing_events& missing() const noexcept { return missing_; }

		/** @brief returns non-mutating iterator to position of
		 *         first argument -> parameter mapping  */
		iterator begin() const noexcept { return arg2param_.begin(); }
		/** @brief returns non-mutating iterator to position one past the
		 *         last argument -> parameter mapping  */
		iterator end()   const noexcept { return arg2param_.end(); }

	private:
		//-----------------------------------------------------
		arg_mappings arg2param_;
		missing_events missing_;
	};




	namespace detail {
		namespace {

			/*************************************************************************//**
			 *
			 * @brief correct some common problems
			 *        does not - and MUST NOT - change the number of arguments
			 *        (no insertions or deletions allowed)
			 *
			 *****************************************************************************/
			void sanitize_args(arg_list& args)
			{
				//e.g. {"-o12", ".34"} -> {"-o", "12.34"}

				if (args.empty()) return;

				for (auto i = begin(args) + 1; i != end(args); ++i) {
					if (i != begin(args) && i->size() > 1 &&
						i->find('.') == 0 && std::isdigit((*i)[1]))
					{
						//find trailing digits in previous arg
						using std::prev;
						auto& prv = *prev(i);
						auto fstDigit = std::find_if_not(prv.rbegin(), prv.rend(),
							[](arg_string::value_type c) {
							return std::isdigit(c);
						}).base();

						//handle leading sign
						if (fstDigit > prv.begin() &&
							(*prev(fstDigit) == '+' || *prev(fstDigit) == '-'))
						{
							--fstDigit;
						}

						//prepend digits from previous arg
						i->insert(begin(*i), fstDigit, end(prv));

						//erase digits in previous arg
						prv.erase(fstDigit, end(prv));
					}
				}
			}



			/*************************************************************************//**
			 *
			 * @brief executes actions based on a parsing result
			 *
			 *****************************************************************************/
			void execute_actions(const parsing_result& res)
			{
				for (const auto& m : res) {
					if (m.param()) {
						const auto& param = *(m.param());

						if (m.repeat() > 0) param.notify_repeated(m.index());
						if (m.blocked())    param.notify_blocked(m.index());
						if (m.conflict())   param.notify_conflict(m.index());
						//main action
						if (!m.any_error()) param.execute_actions(m.arg());
					}
				}

				for (auto m : res.missing()) {
					if (m.param()) m.param()->notify_missing(m.after_index());
				}
			}



			/*************************************************************************//**
			 *
			 * @brief parses input args
			 *
			 *****************************************************************************/
			static parsing_result
				parse_args(const arg_list& args, const group& cli,
					arg_index offset = 0)
			{
				//parse args and store unrecognized arg indices
				parser parse{ cli, offset };
				for (const auto& arg : args) {
					parse(arg);
					if (!parse.valid()) break;
				}

				return parsing_result{ parse.args(), parse.missed() };
			}

			/*************************************************************************//**
			 *
			 * @brief parses input args & executes actions
			 *
			 *****************************************************************************/
			static parsing_result
				parse_and_execute(const arg_list& args, const group& cli,
					arg_index offset = 0)
			{
				auto result = parse_args(args, cli, offset);

				execute_actions(result);

				return result;
			}

		} //anonymous namespace
	} // namespace detail




	/*************************************************************************//**
	 *
	 * @brief parses vector of arg strings and executes actions
	 *
	 *****************************************************************************/
	inline parsing_result
		parse(arg_list args, const group& cli)
	{
		detail::sanitize_args(args);
		return detail::parse_and_execute(args, cli);
	}


	/*************************************************************************//**
	 *
	 * @brief parses initializer_list of C-style arg strings and executes actions
	 *
	 *****************************************************************************/
	inline parsing_result
		parse(std::initializer_list<const char*> arglist, const group& cli)
	{
		arg_list args;
		args.reserve(arglist.size());
		for (auto a : arglist) {
			args.push_back(a);
		}

		return parse(std::move(args), cli);
	}


	/*************************************************************************//**
	 *
	 * @brief parses range of arg strings and executes actions
	 *
	 *****************************************************************************/
	template<class InputIterator>
	inline parsing_result
		parse(InputIterator first, InputIterator last, const group& cli)
	{
		return parse(arg_list(first, last), cli);
	}


	/*************************************************************************//**
	 *
	 * @brief parses the standard array of command line arguments; omits argv[0]
	 *
	 *****************************************************************************/
	inline parsing_result
		parse(const int argc, char* argv[], const group& cli, arg_index offset = 1)
	{
		arg_list args;
		if (offset < argc) args.assign(argv + offset, argv + argc);
		detail::sanitize_args(args);
		return detail::parse_and_execute(args, cli, offset);
	}






	/*************************************************************************//**
	 *
	 * @brief filter predicate for parameters and groups;
	 *        Can be used to limit documentation generation to parameter subsets.
	 *
	 *****************************************************************************/
	class param_filter
	{
	public:
		/** @brief only allow parameters with given prefix */
		param_filter& prefix(const arg_string& p) noexcept {
			prefix_ = p; return *this;
		}
		/** @brief only allow parameters with given prefix */
		param_filter& prefix(arg_string&& p) noexcept {
			prefix_ = std::move(p); return *this;
		}
		const arg_string& prefix()  const noexcept { return prefix_; }

		/** @brief only allow parameters with given requirement status */
		param_filter& required(tri t)  noexcept { required_ = t; return *this; }
		tri           required() const noexcept { return required_; }

		/** @brief only allow parameters with given blocking status */
		param_filter& blocking(tri t)  noexcept { blocking_ = t; return *this; }
		tri           blocking() const noexcept { return blocking_; }

		/** @brief only allow parameters with given repeatable status */
		param_filter& repeatable(tri t)  noexcept { repeatable_ = t; return *this; }
		tri           repeatable() const noexcept { return repeatable_; }

		/** @brief only allow parameters with given docstring status */
		param_filter& has_doc(tri t)  noexcept { hasDoc_ = t; return *this; }
		tri           has_doc() const noexcept { return hasDoc_; }


		/** @brief returns true, if parameter satisfies all filters */
		bool operator() (const parameter& p) const noexcept {
			if (!prefix_.empty()) {
				if (!std::any_of(p.flags().begin(), p.flags().end(),
					[&](const arg_string& flag) {
					return str::has_prefix(flag, prefix_);
				})) return false;
			}
			if (required() != p.required())     return false;
			if (blocking() != p.blocking())     return false;
			if (repeatable() != p.repeatable())   return false;
			if (has_doc() != !p.doc().empty()) return false;
			return true;
		}

	private:
		arg_string prefix_;
		tri required_ = tri::either;
		tri blocking_ = tri::either;
		tri repeatable_ = tri::either;
		tri hasDoc_ = tri::yes;
	};






	/*************************************************************************//**
	 *
	 * @brief documentation formatting options
	 *
	 *****************************************************************************/
	class doc_formatting
	{
	public:
		using string = doc_string;

		/** @brief same as 'first_column' */
#if __cplusplus >= 201402L
		[[deprecated]]
#endif
		doc_formatting& start_column(int col) { return first_column(col); }
#if __cplusplus >= 201402L
		[[deprecated]]
#endif
		int start_column() const noexcept { return first_column(); }

		/** @brief determines column where documentation printing starts */
		doc_formatting&
			first_column(int col) {
			//limit to [0,last_column] but push doc_column to the right if necessary
			if (col < 0) col = 0;
			else if (col > last_column()) col = last_column();
			if (col > doc_column()) doc_column(first_column());
			firstCol_ = col;
			return *this;
		}
		int first_column() const noexcept {
			return firstCol_;
		}

		/** @brief determines column where docstrings start */
		doc_formatting&
			doc_column(int col) {
			//limit to [first_column,last_column]
			if (col < 0) col = 0;
			else if (col < first_column()) col = first_column();
			else if (col > last_column()) col = last_column();
			docCol_ = col;
			return *this;
		}
		int doc_column() const noexcept {
			return docCol_;
		}

		/** @brief determines column that no documentation text must exceed;
		 *         (text should be wrapped appropriately after this column)
		 */
		doc_formatting&
			last_column(int col) {
			//limit to [first_column,oo] but push doc_column to the left if necessary
			if (col < first_column()) col = first_column();
			if (col < doc_column()) doc_column(col);
			lastCol_ = col;
			return *this;
		}

		int last_column() const noexcept {
			return lastCol_;
		}

		/** @brief determines indent of documentation lines
		 *         for children of a documented group */
		doc_formatting& indent_size(int indent) { indentSize_ = indent; return *this; }
		int             indent_size() const noexcept { return indentSize_; }

		/** @brief determines string to be used
		 *         if a parameter has no flags and no label  */
		doc_formatting& empty_label(const string& label) {
			emptyLabel_ = label;
			return *this;
		}
		const string& empty_label() const noexcept { return emptyLabel_; }

		/** @brief determines string for separating parameters */
		doc_formatting& param_separator(const string& sep) {
			paramSep_ = sep;
			return *this;
		}
		const string& param_separator() const noexcept { return paramSep_; }

		/** @brief determines string for separating groups (in usage lines) */
		doc_formatting& group_separator(const string& sep) {
			groupSep_ = sep;
			return *this;
		}
		const string& group_separator() const noexcept { return groupSep_; }

		/** @brief determines string for separating alternative parameters */
		doc_formatting& alternative_param_separator(const string& sep) {
			altParamSep_ = sep;
			return *this;
		}
		const string& alternative_param_separator() const noexcept { return altParamSep_; }

		/** @brief determines string for separating alternative groups */
		doc_formatting& alternative_group_separator(const string& sep) {
			altGroupSep_ = sep;
			return *this;
		}
		const string& alternative_group_separator() const noexcept { return altGroupSep_; }

		/** @brief determines string for separating flags of the same parameter */
		doc_formatting& flag_separator(const string& sep) {
			flagSep_ = sep;
			return *this;
		}
		const string& flag_separator() const noexcept { return flagSep_; }

		/** @brief determines strings surrounding parameter labels */
		doc_formatting&
			surround_labels(const string& prefix, const string& postfix) {
			labelPre_ = prefix;
			labelPst_ = postfix;
			return *this;
		}
		const string& label_prefix()  const noexcept { return labelPre_; }
		const string& label_postfix() const noexcept { return labelPst_; }

		/** @brief determines strings surrounding optional parameters/groups */
		doc_formatting&
			surround_optional(const string& prefix, const string& postfix) {
			optionPre_ = prefix;
			optionPst_ = postfix;
			return *this;
		}
		const string& optional_prefix()  const noexcept { return optionPre_; }
		const string& optional_postfix() const noexcept { return optionPst_; }

		/** @brief determines strings surrounding repeatable parameters/groups */
		doc_formatting&
			surround_repeat(const string& prefix, const string& postfix) {
			repeatPre_ = prefix;
			repeatPst_ = postfix;
			return *this;
		}
		const string& repeat_prefix()  const noexcept { return repeatPre_; }
		const string& repeat_postfix() const noexcept { return repeatPst_; }

		/** @brief determines strings surrounding exclusive groups */
		doc_formatting&
			surround_alternatives(const string& prefix, const string& postfix) {
			alternPre_ = prefix;
			alternPst_ = postfix;
			return *this;
		}
		const string& alternatives_prefix()  const noexcept { return alternPre_; }
		const string& alternatives_postfix() const noexcept { return alternPst_; }

		/** @brief determines strings surrounding alternative flags */
		doc_formatting&
			surround_alternative_flags(const string& prefix, const string& postfix) {
			alternFlagPre_ = prefix;
			alternFlagPst_ = postfix;
			return *this;
		}
		const string& alternative_flags_prefix()  const noexcept { return alternFlagPre_; }
		const string& alternative_flags_postfix() const noexcept { return alternFlagPst_; }

		/** @brief determines strings surrounding non-exclusive groups */
		doc_formatting&
			surround_group(const string& prefix, const string& postfix) {
			groupPre_ = prefix;
			groupPst_ = postfix;
			return *this;
		}
		const string& group_prefix()  const noexcept { return groupPre_; }
		const string& group_postfix() const noexcept { return groupPst_; }

		/** @brief determines strings surrounding joinable groups */
		doc_formatting&
			surround_joinable(const string& prefix, const string& postfix) {
			joinablePre_ = prefix;
			joinablePst_ = postfix;
			return *this;
		}
		const string& joinable_prefix()  const noexcept { return joinablePre_; }
		const string& joinable_postfix() const noexcept { return joinablePst_; }

		/** @brief determines maximum number of flags per parameter to be printed
		 *         in detailed parameter documentation lines */
		doc_formatting& max_flags_per_param_in_doc(int max) {
			maxAltInDocs_ = max > 0 ? max : 0;
			return *this;
		}
		int max_flags_per_param_in_doc() const noexcept { return maxAltInDocs_; }

		/** @brief determines maximum number of flags per parameter to be printed
		 *         in usage lines */
		doc_formatting& max_flags_per_param_in_usage(int max) {
			maxAltInUsage_ = max > 0 ? max : 0;
			return *this;
		}
		int max_flags_per_param_in_usage() const noexcept { return maxAltInUsage_; }

		/** @brief determines number of empty rows after one single-line
		 *         documentation entry */
		doc_formatting& line_spacing(int lines) {
			lineSpc_ = lines > 0 ? lines : 0;
			return *this;
		}
		int line_spacing() const noexcept { return lineSpc_; }

		/** @brief determines number of empty rows before and after a paragraph;
		 *         a paragraph is defined by a documented group or if
		 *         a parameter documentation entry used more than one line */
		doc_formatting& paragraph_spacing(int lines) {
			paragraphSpc_ = lines > 0 ? lines : 0;
			return *this;
		}
		int paragraph_spacing() const noexcept { return paragraphSpc_; }

		/** @brief determines if alternative flags with a common prefix should
		 *         be printed in a merged fashion */
		doc_formatting& merge_alternative_flags_with_common_prefix(bool yes = true) {
			mergeAltCommonPfx_ = yes;
			return *this;
		}
		bool merge_alternative_flags_with_common_prefix() const noexcept {
			return mergeAltCommonPfx_;
		}

		/** @brief determines if joinable flags with a common prefix should
		 *         be printed in a merged fashion */
		doc_formatting& merge_joinable_with_common_prefix(bool yes = true) {
			mergeJoinableCommonPfx_ = yes;
			return *this;
		}
		bool merge_joinable_with_common_prefix() const noexcept {
			return mergeJoinableCommonPfx_;
		}

		/** @brief determines if children of exclusive groups should be printed
		 *         on individual lines if the exceed 'alternatives_min_split_size'
		 */
		doc_formatting& split_alternatives(bool yes = true) {
			splitTopAlt_ = yes;
			return *this;
		}
		bool split_alternatives() const noexcept {
			return splitTopAlt_;
		}

		/** @brief determines how many children exclusive groups can have before
		 *         their children are printed on individual usage lines */
		doc_formatting& alternatives_min_split_size(int size) {
			groupSplitSize_ = size > 0 ? size : 0;
			return *this;
		}
		int alternatives_min_split_size() const noexcept { return groupSplitSize_; }

		/** @brief determines whether to ignore new line characters in docstrings
		 */
		doc_formatting& ignore_newline_chars(bool yes = true) {
			ignoreNewlines_ = yes;
			return *this;
		}
		bool ignore_newline_chars() const noexcept {
			return ignoreNewlines_;
		}

	private:
		string paramSep_ = string(" ");
		string groupSep_ = string(" ");
		string altParamSep_ = string("|");
		string altGroupSep_ = string(" | ");
		string flagSep_ = string(", ");
		string labelPre_ = string("<");
		string labelPst_ = string(">");
		string optionPre_ = string("[");
		string optionPst_ = string("]");
		string repeatPre_ = string("");
		string repeatPst_ = string("...");
		string groupPre_ = string("(");
		string groupPst_ = string(")");
		string alternPre_ = string("(");
		string alternPst_ = string(")");
		string alternFlagPre_ = string("");
		string alternFlagPst_ = string("");
		string joinablePre_ = string("(");
		string joinablePst_ = string(")");
		string emptyLabel_ = string("");
		int firstCol_ = 8;
		int docCol_ = 20;
		int lastCol_ = 100;
		int indentSize_ = 4;
		int maxAltInUsage_ = 1;
		int maxAltInDocs_ = 32;
		int lineSpc_ = 0;
		int paragraphSpc_ = 1;
		int groupSplitSize_ = 3;
		bool splitTopAlt_ = true;
		bool mergeAltCommonPfx_ = false;
		bool mergeJoinableCommonPfx_ = true;
		bool ignoreNewlines_ = false;
	};



	namespace detail {

		/*************************************************************************//**
		 *
		 * @brief stream decorator
		 *        that applies formatting like line wrapping
		 *
		 *****************************************************************************/
		template<class OStream = std::ostream, class StringT = doc_string>
		class formatting_ostream
		{
		public:
			using string_type = StringT;
			using size_type = typename string_type::size_type;
			using char_type = typename string_type::value_type;

			formatting_ostream(OStream& os) :
				os_(os),
				curCol_{ 0 }, firstCol_{ 0 }, lastCol_{ 100 },
				hangingIndent_{ 0 }, paragraphSpacing_{ 0 }, paragraphSpacingThreshold_{ 2 },
				curBlankLines_{ 0 }, curParagraphLines_{ 1 },
				totalNonBlankLines_{ 0 },
				ignoreInputNls_{ false }
			{}


			//---------------------------------------------------------------
			const OStream& base() const noexcept { return os_; }
			OStream& base()       noexcept { return os_; }

			bool good() const { return os_.good(); }


			//---------------------------------------------------------------
			/** @brief determines the leftmost border of the text body */
			formatting_ostream& first_column(int c) {
				firstCol_ = c < 0 ? 0 : c;
				return *this;
			}
			int first_column() const noexcept { return firstCol_; }

			/** @brief determines the rightmost border of the text body */
			formatting_ostream& last_column(int c) {
				lastCol_ = c < 0 ? 0 : c;
				return *this;
			}

			int last_column() const noexcept { return lastCol_; }

			int text_width() const noexcept {
				return lastCol_ - firstCol_;
			}

			/** @brief additional indentation for the 2nd, 3rd, ... line of
					   a paragraph (sequence of soft-wrapped lines) */
			formatting_ostream& hanging_indent(int amount) {
				hangingIndent_ = amount;
				return *this;
			}
			int hanging_indent() const noexcept {
				return hangingIndent_;
			}

			/** @brief amount of blank lines between paragraphs */
			formatting_ostream& paragraph_spacing(int lines) {
				paragraphSpacing_ = lines;
				return *this;
			}
			int paragraph_spacing() const noexcept {
				return paragraphSpacing_;
			}

			/** @brief insert paragraph spacing
					   if paragraph is at least 'lines' lines long */
			formatting_ostream& min_paragraph_lines_for_spacing(int lines) {
				paragraphSpacingThreshold_ = lines;
				return *this;
			}
			int min_paragraph_lines_for_spacing() const noexcept {
				return paragraphSpacingThreshold_;
			}

			/** @brief if set to true, newline characters will be ignored */
			formatting_ostream& ignore_newline_chars(bool yes) {
				ignoreInputNls_ = yes;
				return *this;
			}

			bool ignore_newline_chars() const noexcept {
				return ignoreInputNls_;
			}


			//---------------------------------------------------------------
			/* @brief insert 'n' spaces */
			void write_spaces(int n) {
				if (n < 1) return;
				os_ << string_type(size_type(n), ' ');
				curCol_ += n;
			}

			/* @brief go to new line, but continue current paragraph */
			void wrap_soft(int times = 1) {
				if (times < 1) return;
				if (times > 1) {
					os_ << string_type(size_type(times), '\n');
				}
				else {
					os_ << '\n';
				}
				curCol_ = 0;
				++curParagraphLines_;
			}

			/* @brief go to new line, and start a new paragraph */
			void wrap_hard(int times = 1) {
				if (times < 1) return;

				if (paragraph_spacing() > 0 &&
					paragraph_lines() >= min_paragraph_lines_for_spacing())
				{
					times = paragraph_spacing() + 1;
				}

				if (times > 1) {
					os_ << string_type(size_type(times), '\n');
					curBlankLines_ += times - 1;
				}
				else {
					os_ << '\n';
				}
				if (at_begin_of_line()) {
					++curBlankLines_;
				}
				curCol_ = 0;
				curParagraphLines_ = 1;
			}


			//---------------------------------------------------------------
			bool at_begin_of_line() const noexcept {
				return curCol_ <= current_line_begin();
			}
			int current_line_begin() const noexcept {
				return in_hanging_part_of_paragraph()
					? firstCol_ + hangingIndent_
					: firstCol_;
			}

			int current_column() const noexcept {
				return curCol_;
			}

			int total_non_blank_lines() const noexcept {
				return totalNonBlankLines_;
			}
			int paragraph_lines() const noexcept {
				return curParagraphLines_;
			}
			int blank_lines_before_paragraph() const noexcept {
				return curBlankLines_;
			}


			//---------------------------------------------------------------
			template<class T>
			friend formatting_ostream&
				operator << (formatting_ostream& os, const T& x) {
				os.write(x);
				return os;
			}

			void flush() {
				os_.flush();
			}


		private:
			bool in_hanging_part_of_paragraph() const noexcept {
				return hanging_indent() > 0 && paragraph_lines() > 1;
			}
			bool current_line_empty() const noexcept {
				return curCol_ < 1;
			}
			bool left_of_text_area() const noexcept {
				return curCol_ < current_line_begin();
			}
			bool right_of_text_area() const noexcept {
				return curCol_ > lastCol_;
			}
			int columns_left_in_line() const noexcept {
				return lastCol_ - std::max(current_line_begin(), curCol_);
			}

			void fix_indent() {
				if (left_of_text_area()) {
					const auto fst = current_line_begin();
					write_spaces(fst - curCol_);
					curCol_ = fst;
				}
			}

			template<class Iter>
			bool only_whitespace(Iter first, Iter last) const {
				return last == std::find_if_not(first, last,
					[](char_type c) { return std::isspace(c); });
			}

			/** @brief write any object */
			template<class T>
			void write(const T& x) {
				std::ostringstream ss;
				ss << x;
				write(std::move(ss).str());
			}

			/** @brief write a stringstream */
			void write(const std::ostringstream& s) {
				write(s.str());
			}

			/** @brief write a string */
			void write(const string_type& s) {
				write(s.begin(), s.end());
			}

			/** @brief partition output into lines */
			template<class Iter>
			void write(Iter first, Iter last)
			{
				if (first == last) return;
				if (*first == '\n') {
					if (!ignore_newline_chars()) wrap_hard();
					++first;
					if (first == last) return;
				}
				auto i = std::find(first, last, '\n');
				if (i != last) {
					if (ignore_newline_chars()) ++i;
					if (i != last) {
						write_line(first, i);
						write(i, last);
					}
				}
				else {
					write_line(first, last);
				}
			}

			/** @brief handle line wrapping due to column constraints */
			template<class Iter>
			void write_line(Iter first, Iter last)
			{
				if (first == last) return;
				if (only_whitespace(first, last)) return;

				if (right_of_text_area()) wrap_soft();

				if (at_begin_of_line()) {
					//discard whitespace, it we start a new line
					first = std::find_if(first, last,
						[](char_type c) { return !std::isspace(c); });
					if (first == last) return;
				}

				const auto n = int(std::distance(first, last));
				const auto m = columns_left_in_line();
				//if text to be printed is too long for one line -> wrap
				if (n > m) {
					//break before word, if break is mid-word
					auto breakat = first + m;
					while (breakat > first && !std::isspace(*breakat)) --breakat;
					//could not find whitespace before word -> try after the word
					if (!std::isspace(*breakat) && breakat == first) {
						breakat = std::find_if(first + m, last,
							[](char_type c) { return std::isspace(c); });
					}
					if (breakat > first) {
						if (curCol_ < 1) ++totalNonBlankLines_;
						fix_indent();
						std::copy(first, breakat, std::ostream_iterator<char_type>(os_));
						curBlankLines_ = 0;
					}
					if (breakat < last) {
						wrap_soft();
						write_line(breakat, last);
					}
				}
				else {
					if (curCol_ < 1) ++totalNonBlankLines_;
					fix_indent();
					std::copy(first, last, std::ostream_iterator<char_type>(os_));
					curCol_ += n;
					curBlankLines_ = 0;
				}
			}

			/** @brief write a single character */
			void write(char_type c)
			{
				if (c == '\n') {
					if (!ignore_newline_chars()) wrap_hard();
				}
				else {
					if (at_begin_of_line()) ++totalNonBlankLines_;
					fix_indent();
					os_ << c;
					++curCol_;
				}
			}

			OStream& os_;
			int curCol_;
			int firstCol_;
			int lastCol_;
			int hangingIndent_;
			int paragraphSpacing_;
			int paragraphSpacingThreshold_;
			int curBlankLines_;
			int curParagraphLines_;
			int totalNonBlankLines_;
			bool ignoreInputNls_;
		};


	}




	/*************************************************************************//**
	 *
	 * @brief   generates usage lines
	 *
	 * @details lazily evaluated
	 *
	 *****************************************************************************/
	class usage_lines
	{
	public:
		using string = doc_string;

		usage_lines(const group& cli, string prefix = "",
			const doc_formatting& fmt = doc_formatting{})
			:
			cli_(cli), fmt_(fmt), prefix_(std::move(prefix))
		{
			if (!prefix_.empty()) prefix_ += ' ';
		}

		usage_lines(const group& cli, const doc_formatting& fmt) :
			usage_lines(cli, "", fmt)
		{}

		usage_lines& ommit_outermost_group_surrounders(bool yes) {
			ommitOutermostSurrounders_ = yes;
			return *this;
		}
		bool ommit_outermost_group_surrounders() const {
			return ommitOutermostSurrounders_;
		}

		template<class OStream>
		inline friend OStream& operator << (OStream& os, const usage_lines& p) {
			p.write(os);
			return os;
		}

		string str() const {
			std::ostringstream os; os << *this; return os.str();
		}


	private:
		using stream_t = detail::formatting_ostream<>;
		const group& cli_;
		doc_formatting fmt_;
		string prefix_;
		bool ommitOutermostSurrounders_ = false;


		//-----------------------------------------------------
		struct context {
			group::depth_first_traverser pos;
			std::stack<string> separators;
			std::stack<string> postfixes;
			int level = 0;
			const group* outermost = nullptr;
			bool linestart = false;
			bool useOutermost = true;
			int line = 0;

			bool is_singleton() const noexcept {
				return linestart && pos.is_last_in_path();
			}
			bool is_alternative() const noexcept {
				return pos.parent().exclusive();
			}
		};


		/***************************************************************//**
		 *
		 * @brief writes usage text for command line parameters
		 *
		 *******************************************************************/
		template<class OStream>
		void write(OStream& os) const
		{
			detail::formatting_ostream<OStream> fos(os);
			fos.first_column(fmt_.first_column());
			fos.last_column(fmt_.last_column());

			auto hindent = int(prefix_.size());
			if (fos.first_column() + hindent >= int(0.4 * fos.text_width())) {
				hindent = fmt_.indent_size();
			}
			fos.hanging_indent(hindent);

			fos.paragraph_spacing(fmt_.paragraph_spacing());
			fos.min_paragraph_lines_for_spacing(2);
			fos.ignore_newline_chars(fmt_.ignore_newline_chars());

			context cur;
			cur.pos = cli_.begin_dfs();
			cur.linestart = true;
			cur.level = cur.pos.level();
			cur.outermost = &cli_;

			write(fos, cur, prefix_);
		}


		/***************************************************************//**
		 *
		 * @brief writes usage text for command line parameters
		 *
		 * @param prefix   all that goes in front of current things to print
		 *
		 *******************************************************************/
		template<class OStream>
		void write(OStream& os, context cur, string prefix) const
		{
			if (!cur.pos) return;

			std::ostringstream buf;
			if (cur.linestart) buf << prefix;
			const auto initPos = buf.tellp();

			cur.level = cur.pos.level();

			if (cur.useOutermost) {
				//we cannot start outside of the outermost group
				//so we have to treat it separately
				start_group(buf, cur.pos.parent(), cur);
				if (!cur.pos) {
					os << buf.str();
					return;
				}
			}
			else {
				//don't visit siblings of starter node
				cur.pos.skip_siblings();
			}
			check_end_group(buf, cur);

			do {
				if (buf.tellp() > initPos) cur.linestart = false;
				if (!cur.linestart && !cur.pos.is_first_in_parent()) {
					buf << cur.separators.top();
				}
				if (cur.pos->is_group()) {
					start_group(buf, cur.pos->as_group(), cur);
					if (!cur.pos) {
						os << buf.str();
						return;
					}
				}
				else {
					buf << param_label(cur.pos->as_param(), cur);
					++cur.pos;
				}
				check_end_group(buf, cur);
			} while (cur.pos);

			os << buf.str();
		}


		/***************************************************************//**
		 *
		 * @brief handles pattern group surrounders and separators
		 *        and alternative splitting
		 *
		 *******************************************************************/
		void start_group(std::ostringstream& os,
			const group& group, context& cur) const
		{
			//does cur.pos already point to a member or to group itself?
			//needed for special treatment of outermost group
			const bool alreadyInside = &(cur.pos.parent()) == &group;

			auto lbl = joined_label(group, cur);
			if (!lbl.empty()) {
				os << lbl;
				cur.linestart = false;
				//skip over entire group as its label has already been created
				if (alreadyInside) {
					cur.pos.next_after_siblings();
				}
				else {
					cur.pos.next_sibling();
				}
			}
			else {
				const bool splitAlternatives = group.exclusive() &&
					fmt_.split_alternatives() &&
					std::any_of(group.begin(), group.end(),
						[this](const pattern& p) {
					return int(p.param_count()) >= fmt_.alternatives_min_split_size();
				});

				if (splitAlternatives) {
					cur.postfixes.push("");
					cur.separators.push("");
					//recursively print alternative paths in decision-DAG
					//enter group?
					if (!alreadyInside) ++cur.pos;
					cur.linestart = true;
					cur.useOutermost = false;
					auto pfx = os.str();
					os.str("");
					//print paths in DAG starting at each group member
					for (std::size_t i = 0; i < group.size(); ++i) {
						std::stringstream buf;
						cur.outermost = cur.pos->is_group() ? &(cur.pos->as_group()) : nullptr;
						write(buf, cur, pfx);
						if (buf.tellp() > int(pfx.size())) {
							os << buf.str();
							if (i < group.size() - 1) {
								if (cur.line > 0) {
									os << string(fmt_.line_spacing(), '\n');
								}
								++cur.line;
								os << '\n';
							}
						}
						cur.pos.next_sibling(); //do not descend into members
					}
					cur.pos.invalidate(); //signal end-of-path
					return;
				}
				else {
					//pre & postfixes, separators
					auto surround = group_surrounders(group, cur);
					os << surround.first;
					cur.postfixes.push(std::move(surround.second));
					cur.separators.push(group_separator(group, fmt_));
					//descend into group?
					if (!alreadyInside) ++cur.pos;
				}
			}
			cur.level = cur.pos.level();
		}


		/***************************************************************//**
		 *
		 *******************************************************************/
		void check_end_group(std::ostringstream& os, context& cur) const
		{
			for (; cur.level > cur.pos.level(); --cur.level) {
				os << cur.postfixes.top();
				cur.postfixes.pop();
				cur.separators.pop();
			}
			cur.level = cur.pos.level();
		}


		/***************************************************************//**
		 *
		 * @brief makes usage label for one command line parameter
		 *
		 *******************************************************************/
		string param_label(const parameter& p, const context& cur) const
		{
			const auto& parent = cur.pos.parent();

			const bool startsOptionalSequence =
				parent.size() > 1 && p.blocking() && cur.pos.is_first_in_parent();

			const bool outermost =
				ommitOutermostSurrounders_ && cur.outermost == &parent;

			const bool showopt = !cur.is_alternative() && !p.required()
				&& !startsOptionalSequence && !outermost;

			const bool showrep = p.repeatable() && !outermost;

			string lbl;

			if (showrep) lbl += fmt_.repeat_prefix();
			if (showopt) lbl += fmt_.optional_prefix();

			const auto& flags = p.flags();
			if (!flags.empty()) {
				const int n = std::min(fmt_.max_flags_per_param_in_usage(),
					int(flags.size()));

				const bool surrAlt = n > 1 && !showopt && !cur.is_singleton();

				if (surrAlt) lbl += fmt_.alternative_flags_prefix();
				bool sep = false;
				for (int i = 0; i < n; ++i) {
					if (sep) {
						if (cur.is_singleton())
							lbl += fmt_.alternative_group_separator();
						else
							lbl += fmt_.flag_separator();
					}
					lbl += flags[i];
					sep = true;
				}
				if (surrAlt) lbl += fmt_.alternative_flags_postfix();
			}
			else {
				if (!p.label().empty()) {
					lbl += fmt_.label_prefix()
						+ p.label()
						+ fmt_.label_postfix();
				}
				else if (!fmt_.empty_label().empty()) {
					lbl += fmt_.label_prefix()
						+ fmt_.empty_label()
						+ fmt_.label_postfix();
				}
				else {
					return "";
				}
			}

			if (showopt) lbl += fmt_.optional_postfix();
			if (showrep) lbl += fmt_.repeat_postfix();

			return lbl;
		}


		/***************************************************************//**
		 *
		 * @brief prints flags in one group in a merged fashion
		 *
		 *******************************************************************/
		string joined_label(const group& g, const context& cur) const
		{
			if (!fmt_.merge_alternative_flags_with_common_prefix() &&
				!fmt_.merge_joinable_with_common_prefix()) return "";

			const bool flagsonly = std::all_of(g.begin(), g.end(),
				[](const pattern& p) {
				return p.is_param() && !p.as_param().flags().empty();
			});

			if (!flagsonly) return "";

			const bool showOpt = g.all_optional() &&
				!(ommitOutermostSurrounders_ && cur.outermost == &g);

			auto pfx = g.common_flag_prefix();
			if (pfx.empty()) return "";

			const auto n = pfx.size();
			if (g.exclusive() &&
				fmt_.merge_alternative_flags_with_common_prefix())
			{
				string lbl;
				if (showOpt) lbl += fmt_.optional_prefix();
				lbl += pfx + fmt_.alternatives_prefix();
				bool first = true;
				for (const auto& p : g) {
					if (p.is_param()) {
						if (first)
							first = false;
						else
							lbl += fmt_.alternative_param_separator();
						lbl += p.as_param().flags().front().substr(n);
					}
				}
				lbl += fmt_.alternatives_postfix();
				if (showOpt) lbl += fmt_.optional_postfix();
				return lbl;
			}
			//no alternatives, but joinable flags
			else if (g.joinable() &&
				fmt_.merge_joinable_with_common_prefix())
			{
				const bool allSingleChar = std::all_of(g.begin(), g.end(),
					[&](const pattern& p) {
					return p.is_param() &&
						p.as_param().flags().front().substr(n).size() == 1;
				});

				if (allSingleChar) {
					string lbl;
					if (showOpt) lbl += fmt_.optional_prefix();
					lbl += pfx;
					for (const auto& p : g) {
						if (p.is_param())
							lbl += p.as_param().flags().front().substr(n);
					}
					if (showOpt) lbl += fmt_.optional_postfix();
					return lbl;
				}
			}

			return "";
		}


		/***************************************************************//**
		 *
		 * @return symbols with which to surround a group
		 *
		 *******************************************************************/
		std::pair<string, string>
			group_surrounders(const group& group, const context& cur) const
		{
			string prefix;
			string postfix;

			const bool isOutermost = &group == cur.outermost;
			if (isOutermost && ommitOutermostSurrounders_)
				return { string{}, string{} };

			if (group.exclusive()) {
				if (group.all_optional()) {
					prefix = fmt_.optional_prefix();
					postfix = fmt_.optional_postfix();
					if (group.all_flagless()) {
						prefix += fmt_.label_prefix();
						postfix = fmt_.label_prefix() + postfix;
					}
				}
				else if (group.all_flagless()) {
					prefix = fmt_.label_prefix();
					postfix = fmt_.label_postfix();
				}
				else if (!cur.is_singleton() || !isOutermost) {
					prefix = fmt_.alternatives_prefix();
					postfix = fmt_.alternatives_postfix();
				}
			}
			else if (group.size() > 1 &&
				group.front().blocking() && !group.front().required())
			{
				prefix = fmt_.optional_prefix();
				postfix = fmt_.optional_postfix();
			}
			else if (group.size() > 1 && cur.is_alternative() &&
				&group != cur.outermost)
			{
				prefix = fmt_.group_prefix();
				postfix = fmt_.group_postfix();
			}
			else if (!group.exclusive() &&
				group.joinable() && !cur.linestart)
			{
				prefix = fmt_.joinable_prefix();
				postfix = fmt_.joinable_postfix();
			}

			if (group.repeatable()) {
				if (prefix.empty()) prefix = fmt_.group_prefix();
				prefix = fmt_.repeat_prefix() + prefix;
				if (postfix.empty()) postfix = fmt_.group_postfix();
				postfix += fmt_.repeat_postfix();
			}

			return { std::move(prefix), std::move(postfix) };
		}


		/***************************************************************//**
		 *
		 * @return symbol that separates members of a group
		 *
		 *******************************************************************/
		static string
			group_separator(const group& group, const doc_formatting& fmt)
		{
			const bool only1ParamPerMember = std::all_of(group.begin(), group.end(),
				[](const pattern& p) { return p.param_count() < 2; });

			if (only1ParamPerMember) {
				if (group.exclusive()) {
					return fmt.alternative_param_separator();
				}
				else {
					return fmt.param_separator();
				}
			}
			else { //there is at least one large group inside
				if (group.exclusive()) {
					return fmt.alternative_group_separator();
				}
				else {
					return fmt.group_separator();
				}
			}
		}
	};




	/*************************************************************************//**
	 *
	 * @brief   generates parameter and group documentation from docstrings
	 *
	 * @details lazily evaluated
	 *
	 *****************************************************************************/
	class documentation
	{
	public:
		using string = doc_string;
		using filter_function = std::function<bool(const parameter&)>;

		documentation(const group& cli,
			const doc_formatting& fmt = doc_formatting{},
			filter_function filter = param_filter{})
			:
			cli_(cli), fmt_{ fmt }, usgFmt_{ fmt }, filter_{ std::move(filter) }
		{
			//necessary, because we re-use "usage_lines" to generate
			//labels for documented groups
			usgFmt_.max_flags_per_param_in_usage(
				usgFmt_.max_flags_per_param_in_doc());
		}

		documentation(const group& cli, filter_function filter) :
			documentation{ cli, doc_formatting{}, std::move(filter) }
		{}

		documentation(const group& cli, const param_filter& filter) :
			documentation{ cli, doc_formatting{},
						  [filter](const parameter& p) { return filter(p); } }
		{}

		template<class OStream>
		inline friend OStream& operator << (OStream& os, const documentation& p) {
			p.write(os);
			return os;
		}

		string str() const {
			std::ostringstream os;
			write(os);
			return os.str();
		}


	private:
		using dfs_traverser = group::depth_first_traverser;

		const group& cli_;
		doc_formatting fmt_;
		doc_formatting usgFmt_;
		filter_function filter_;
		enum class paragraph { param, group };


		/***************************************************************//**
		 *
		 * @brief writes documentation to output stream
		 *
		 *******************************************************************/
		template<class OStream>
		void write(OStream& os) const {
			detail::formatting_ostream<OStream> fos(os);
			fos.first_column(fmt_.first_column());
			fos.last_column(fmt_.last_column());
			fos.hanging_indent(0);
			fos.paragraph_spacing(0);
			fos.ignore_newline_chars(fmt_.ignore_newline_chars());
			print_doc(fos, cli_);
		}


		/***************************************************************//**
		 *
		 * @brief writes full documentation text for command line parameters
		 *
		 *******************************************************************/
		template<class OStream>
		void print_doc(detail::formatting_ostream<OStream>& os,
			const group& cli, int indentLvl = 0) const
		{
			if (cli.empty()) return;

			//if group itself doesn't have docstring
			if (cli.doc().empty()) {
				for (const auto& p : cli) {
					print_doc(os, p, indentLvl);
				}
			}
			else { //group itself does have docstring
				bool anyDocInside = std::any_of(cli.begin(), cli.end(),
					[](const pattern& p) { return !p.doc().empty(); });

				if (anyDocInside) { //group docstring as title, then child entries
					handle_spacing(os, paragraph::group, indentLvl);
					os << cli.doc();
					for (const auto& p : cli) {
						print_doc(os, p, indentLvl + 1);
					}
				}
				else { //group label first then group docstring
					auto lbl = usage_lines(cli, usgFmt_)
						.ommit_outermost_group_surrounders(true).str();

					str::trim(lbl);
					handle_spacing(os, paragraph::param, indentLvl);
					print_entry(os, lbl, cli.doc());
				}
			}
		}


		/***************************************************************//**
		 *
		 * @brief writes documentation text for one group or parameter
		 *
		 *******************************************************************/
		template<class OStream>
		void print_doc(detail::formatting_ostream<OStream>& os,
			const pattern& ptrn, int indentLvl) const
		{
			if (ptrn.is_group()) {
				print_doc(os, ptrn.as_group(), indentLvl);
			}
			else {
				const auto& p = ptrn.as_param();
				if (!filter_(p)) return;

				handle_spacing(os, paragraph::param, indentLvl);
				print_entry(os, param_label(p, fmt_), p.doc());
			}
		}

		/***************************************************************//**
		 *
		 * @brief handles line and paragraph spacings
		 *
		 *******************************************************************/
		template<class OStream>
		void handle_spacing(detail::formatting_ostream<OStream>& os,
			paragraph p, int indentLvl) const
		{
			const auto oldIndent = os.first_column();
			const auto indent = fmt_.first_column() + indentLvl * fmt_.indent_size();

			if (os.total_non_blank_lines() < 1) {
				os.first_column(indent);
				return;
			}

			if (os.paragraph_lines() > 1 || indent < oldIndent) {
				os.wrap_hard(fmt_.paragraph_spacing() + 1);
			}
			else {
				os.wrap_hard();
			}

			if (p == paragraph::group) {
				if (os.blank_lines_before_paragraph() < fmt_.paragraph_spacing()) {
					os.wrap_hard(fmt_.paragraph_spacing() - os.blank_lines_before_paragraph());
				}
			}
			else if (os.blank_lines_before_paragraph() < fmt_.line_spacing()) {
				os.wrap_hard(fmt_.line_spacing() - os.blank_lines_before_paragraph());
			}
			os.first_column(indent);
		}

		/*********************************************************************//**
		 *
		 * @brief prints one entry = label + docstring
		 *
		 ************************************************************************/
		template<class OStream>
		void print_entry(detail::formatting_ostream<OStream>& os,
			const string& label, const string& docstr) const
		{
			if (label.empty()) return;

			os << label;

			if (!docstr.empty()) {
				if (os.current_column() >= fmt_.doc_column()) os.wrap_soft();
				const auto oldcol = os.first_column();
				os.first_column(fmt_.doc_column());
				os << docstr;
				os.first_column(oldcol);
			}
		}


		/*********************************************************************//**
		 *
		 * @brief makes label for one parameter
		 *
		 ************************************************************************/
		static doc_string
			param_label(const parameter& param, const doc_formatting& fmt)
		{
			doc_string lbl;

			if (param.repeatable()) lbl += fmt.repeat_prefix();

			const auto& flags = param.flags();
			if (!flags.empty()) {
				lbl += flags[0];
				const int n = std::min(fmt.max_flags_per_param_in_doc(),
					int(flags.size()));
				for (int i = 1; i < n; ++i) {
					lbl += fmt.flag_separator() + flags[i];
				}
			}
			else if (!param.label().empty() || !fmt.empty_label().empty()) {
				lbl += fmt.label_prefix();
				if (!param.label().empty()) {
					lbl += param.label();
				}
				else {
					lbl += fmt.empty_label();
				}
				lbl += fmt.label_postfix();
			}

			if (param.repeatable()) lbl += fmt.repeat_postfix();

			return lbl;
		}

	};




	/*************************************************************************//**
	 *
	 * @brief stores strings for man page sections
	 *
	 *****************************************************************************/
	class man_page
	{
	public:
		//---------------------------------------------------------------
		using string = doc_string;

		//---------------------------------------------------------------
		/** @brief man page section */
		class section {
		public:
			using string = doc_string;

			section(string stitle, string scontent) :
				title_{ std::move(stitle) }, content_{ std::move(scontent) }
			{}

			const string& title()   const noexcept { return title_; }
			const string& content() const noexcept { return content_; }

		private:
			string title_;
			string content_;
		};

	private:
		using section_store = std::vector<section>;

	public:
		//---------------------------------------------------------------
		using value_type = section;
		using const_iterator = section_store::const_iterator;
		using size_type = section_store::size_type;


		//---------------------------------------------------------------
		man_page&
			append_section(string title, string content)
		{
			sections_.emplace_back(std::move(title), std::move(content));
			return *this;
		}
		//-----------------------------------------------------
		man_page&
			prepend_section(string title, string content)
		{
			sections_.emplace(sections_.begin(),
				std::move(title), std::move(content));
			return *this;
		}


		//---------------------------------------------------------------
		const section& operator [] (size_type index) const noexcept {
			return sections_[index];
		}

		//---------------------------------------------------------------
		size_type size() const noexcept { return sections_.size(); }

		bool empty() const noexcept { return sections_.empty(); }


		//---------------------------------------------------------------
		const_iterator begin() const noexcept { return sections_.begin(); }
		const_iterator end()   const noexcept { return sections_.end(); }


		//---------------------------------------------------------------
		man_page& program_name(const string& n) {
			progName_ = n;
			return *this;
		}
		man_page& program_name(string&& n) {
			progName_ = std::move(n);
			return *this;
		}
		const string& program_name() const noexcept {
			return progName_;
		}


		//---------------------------------------------------------------
		man_page& section_row_spacing(int rows) {
			sectionSpc_ = rows > 0 ? rows : 0;
			return *this;
		}
		int section_row_spacing() const noexcept { return sectionSpc_; }


	private:
		int sectionSpc_ = 1;
		section_store sections_;
		string progName_;
	};



	/*************************************************************************//**
	 *
	 * @brief generates man sections from command line parameters
	 *        with sections "synopsis" and "options"
	 *
	 *****************************************************************************/
	inline man_page
		make_man_page(const group& cli,
			doc_string progname = "",
			const doc_formatting& fmt = doc_formatting{})
	{
		man_page man;
		man.append_section("SYNOPSIS", usage_lines(cli, progname, fmt).str());
		man.append_section("OPTIONS", documentation(cli, fmt).str());
		return man;
	}



	/*************************************************************************//**
	 *
	 * @brief   generates man page based on command line parameters
	 *
	 *****************************************************************************/
	template<class OStream>
	OStream&
		operator << (OStream& os, const man_page& man)
	{
		bool first = true;
		const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n');
		for (const auto& section : man) {
			if (!section.content().empty()) {
				if (first) first = false; else os << secSpc;
				if (!section.title().empty()) os << section.title() << '\n';
				os << section.content();
			}
		}
		os << '\n';
		return os;
	}





	/*************************************************************************//**
	 *
	 * @brief printing methods for debugging command line interfaces
	 *
	 *****************************************************************************/
	namespace debug {


		/*************************************************************************//**
		 *
		 * @brief prints first flag or value label of a parameter
		 *
		 *****************************************************************************/
		inline doc_string doc_label(const parameter& p)
		{
			if (!p.flags().empty()) return p.flags().front();
			if (!p.label().empty()) return p.label();
			return doc_string{ "<?>" };
		}

		inline doc_string doc_label(const group&)
		{
			return "<group>";
		}

		inline doc_string doc_label(const pattern& p)
		{
			return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param());
		}


		/*************************************************************************//**
		 *
		 * @brief prints parsing result
		 *
		 *****************************************************************************/
		template<class OStream>
		void print(OStream& os, const parsing_result& result)
		{
			for (const auto& m : result) {
				os << "#" << m.index() << " " << m.arg() << " -> ";
				auto p = m.param();
				if (p) {
					os << doc_label(*p) << " \t";
					if (m.repeat() > 0) {
						os << (m.bad_repeat() ? "[bad repeat " : "[repeat ")
							<< m.repeat() << "]";
					}
					if (m.blocked())  os << " [blocked]";
					if (m.conflict()) os << " [conflict]";
					os << '\n';
				}
				else {
					os << " [unmapped]\n";
				}
			}

			for (const auto& m : result.missing()) {
				auto p = m.param();
				if (p) {
					os << doc_label(*p) << " \t";
					os << " [missing after " << m.after_index() << "]\n";
				}
			}
		}


		/*************************************************************************//**
		 *
		 * @brief prints parameter label and some properties
		 *
		 *****************************************************************************/
		template<class OStream>
		void print(OStream& os, const parameter& p)
		{
			if (p.greedy()) os << '!';
			if (p.blocking()) os << '~';
			if (!p.required()) os << '[';
			os << doc_label(p);
			if (p.repeatable()) os << "...";
			if (!p.required()) os << "]";
		}


		//-------------------------------------------------------------------
		template<class OStream>
		void print(OStream& os, const group& g, int level = 0);


		/*************************************************************************//**
		 *
		 * @brief prints group or parameter; uses indentation
		 *
		 *****************************************************************************/
		template<class OStream>
		void print(OStream& os, const pattern& param, int level = 0)
		{
			if (param.is_group()) {
				print(os, param.as_group(), level);
			}
			else {
				os << doc_string(4 * level, ' ');
				print(os, param.as_param());
			}
		}


		/*************************************************************************//**
		 *
		 * @brief prints group and its contents; uses indentation
		 *
		 *****************************************************************************/
		template<class OStream>
		void print(OStream& os, const group& g, int level)
		{
			auto indent = doc_string(4 * level, ' ');
			os << indent;
			if (g.blocking()) os << '~';
			if (g.joinable()) os << 'J';
			os << (g.exclusive() ? "(|\n" : "(\n");
			for (const auto& p : g) {
				print(os, p, level + 1);
			}
			os << '\n' << indent << (g.exclusive() ? "|)" : ")");
			if (g.repeatable()) os << "...";
			os << '\n';
		}


	} // namespace debug
} //namespace clipp

#endif

